X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/04b8595b18b1b41ac7a206e4b3d51a635f8413d7..ecc0ceb4089d506a0b8d16686a95817b331af9cb:/bsd/kern/kern_control.c diff --git a/bsd/kern/kern_control.c b/bsd/kern/kern_control.c index 8e5b0150b..ebda4203d 100644 --- a/bsd/kern/kern_control.c +++ b/bsd/kern/kern_control.c @@ -50,12 +50,50 @@ #include #include #include +#include #include #include #include +struct kctl { + TAILQ_ENTRY(kctl) next; /* controller chain */ + kern_ctl_ref kctlref; + + /* controller information provided when registering */ + char name[MAX_KCTL_NAME]; /* unique identifier */ + u_int32_t id; + u_int32_t reg_unit; + + /* misc communication information */ + u_int32_t flags; /* support flags */ + u_int32_t recvbufsize; /* request more than the default buffer size */ + u_int32_t sendbufsize; /* request more than the default buffer size */ + + /* Dispatch functions */ + ctl_connect_func connect; /* Make contact */ + ctl_disconnect_func disconnect; /* Break contact */ + ctl_send_func send; /* Send data to nke */ + ctl_send_list_func send_list; /* Send list of packets */ + ctl_setopt_func setopt; /* set kctl configuration */ + ctl_getopt_func getopt; /* get kctl configuration */ + ctl_rcvd_func rcvd; /* Notify nke when client reads data */ + + TAILQ_HEAD(, ctl_cb) kcb_head; + u_int32_t lastunit; +}; + +struct ctl_cb { + TAILQ_ENTRY(ctl_cb) next; /* controller chain */ + lck_mtx_t *mtx; + struct socket *so; /* controlling socket */ + struct kctl *kctl; /* back pointer to controller */ + void *userdata; + u_int32_t unit; + u_int32_t usecount; +}; + #ifndef ROUNDUP64 #define ROUNDUP64(x) P2ROUNDUP((x), sizeof (u_int64_t)) #endif @@ -84,7 +122,6 @@ static lck_mtx_t *ctl_mtx; /* all the controllers are chained */ TAILQ_HEAD(kctl_list, kctl) ctl_head; - static int ctl_attach(struct socket *, int, struct proc *); static int ctl_detach(struct socket *); static int ctl_sofreelastref(struct socket *so); @@ -103,7 +140,8 @@ static int ctl_usr_rcvd(struct socket *so, int flags); static struct kctl *ctl_find_by_name(const char *); static struct kctl *ctl_find_by_id_unit(u_int32_t id, u_int32_t unit); -static struct socket *kcb_find_socket(struct kctl *, u_int32_t unit); +static struct socket *kcb_find_socket(kern_ctl_ref kctlref, u_int32_t unit, + u_int32_t *); static struct ctl_cb *kcb_find(struct kctl *, u_int32_t unit); static void ctl_post_msg(u_int32_t event_code, u_int32_t id); @@ -154,7 +192,6 @@ __private_extern__ int kctl_reg_list SYSCTL_HANDLER_ARGS; __private_extern__ int kctl_pcblist SYSCTL_HANDLER_ARGS; __private_extern__ int kctl_getstat SYSCTL_HANDLER_ARGS; -static int kctl_proto_count = (sizeof (kctlsw) / sizeof (struct protosw)); SYSCTL_NODE(_net_systm, OID_AUTO, kctl, CTLFLAG_RW|CTLFLAG_LOCKED, 0, "Kernel control family"); @@ -184,6 +221,19 @@ u_int32_t ctl_debug = 0; SYSCTL_INT(_net_systm_kctl, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_LOCKED, &ctl_debug, 0, ""); +#define KCTL_TBL_INC 16 + +static uintptr_t kctl_tbl_size = 0; +static u_int32_t kctl_tbl_growing = 0; +static uintptr_t kctl_tbl_count = 0; +static struct kctl **kctl_table = NULL; +static uintptr_t kctl_ref_gencnt = 0; + +static void kctl_tbl_grow(void); +static kern_ctl_ref kctl_make_ref(struct kctl *kctl); +static void kctl_delete_ref(kern_ctl_ref); +static struct kctl *kctl_from_ref(kern_ctl_ref); + /* * Install the protosw's for the Kernel Control manager. */ @@ -192,6 +242,7 @@ kern_control_init(struct domain *dp) { struct protosw *pr; int i; + int kctl_proto_count = (sizeof (kctlsw) / sizeof (struct protosw)); VERIFY(!(dp->dom_flags & DOM_INITIALIZED)); VERIFY(dp == systemdomain); @@ -307,7 +358,6 @@ ctl_detach(struct socket *so) return (0); } - static int ctl_connect(struct socket *so, struct sockaddr *nam, struct proc *p) { @@ -419,7 +469,7 @@ ctl_connect(struct socket *so, struct sockaddr *nam, struct proc *p) soisconnecting(so); socket_unlock(so, 0); - error = (*kctl->connect)(kctl, &sa, &kcb->userdata); + error = (*kctl->connect)(kctl->kctlref, &sa, &kcb->userdata); socket_lock(so, 0); if (error) goto end; @@ -429,7 +479,7 @@ ctl_connect(struct socket *so, struct sockaddr *nam, struct proc *p) end: if (error && kctl->disconnect) { socket_unlock(so, 0); - (*kctl->disconnect)(kctl, kcb->unit, kcb->userdata); + (*kctl->disconnect)(kctl->kctlref, kcb->unit, kcb->userdata); socket_lock(so, 0); } done: @@ -457,7 +507,8 @@ ctl_disconnect(struct socket *so) if (kctl && kctl->disconnect) { socket_unlock(so, 0); - (*kctl->disconnect)(kctl, kcb->unit, kcb->userdata); + (*kctl->disconnect)(kctl->kctlref, kcb->unit, + kcb->userdata); socket_lock(so, 0); } @@ -547,7 +598,7 @@ ctl_usr_rcvd(struct socket *so, int flags) if (kctl->rcvd) { socket_unlock(so, 0); - (*kctl->rcvd)(kctl, kcb->unit, kcb->userdata, flags); + (*kctl->rcvd)(kctl->kctlref, kcb->unit, kcb->userdata, flags); socket_lock(so, 0); } @@ -578,7 +629,8 @@ ctl_send(struct socket *so, int flags, struct mbuf *m, if (error == 0 && kctl->send) { so_tc_update_stats(m, so, m_get_service_class(m)); socket_unlock(so, 0); - error = (*kctl->send)(kctl, kcb->unit, kcb->userdata, m, flags); + error = (*kctl->send)(kctl->kctlref, kcb->unit, kcb->userdata, + m, flags); socket_lock(so, 0); } else { m_freem(m); @@ -615,8 +667,8 @@ ctl_send_list(struct socket *so, int flags, struct mbuf *m, so_tc_update_stats(nxt, so, m_get_service_class(nxt)); socket_unlock(so, 0); - error = (*kctl->send_list)(kctl, kcb->unit, kcb->userdata, m, - flags); + error = (*kctl->send_list)(kctl->kctlref, kcb->unit, + kcb->userdata, m, flags); socket_lock(so, 0); } else if (error == 0 && kctl->send) { while (m != NULL && error == 0) { @@ -625,8 +677,8 @@ ctl_send_list(struct socket *so, int flags, struct mbuf *m, m->m_nextpkt = NULL; so_tc_update_stats(m, so, m_get_service_class(m)); socket_unlock(so, 0); - error = (*kctl->send)(kctl, kcb->unit, kcb->userdata, m, - flags); + error = (*kctl->send)(kctl->kctlref, kcb->unit, + kcb->userdata, m, flags); socket_lock(so, 0); m = nextpkt; } @@ -643,27 +695,27 @@ ctl_send_list(struct socket *so, int flags, struct mbuf *m, } static errno_t -ctl_rcvbspace(struct kctl *kctl, struct socket *so, u_int32_t datasize, - u_int32_t flags) +ctl_rcvbspace(struct socket *so, u_int32_t datasize, + u_int32_t kctlflags, u_int32_t flags) { struct sockbuf *sb = &so->so_rcv; u_int32_t space = sbspace(sb); errno_t error; - if ((kctl->flags & CTL_FLAG_REG_CRIT) == 0) { + if ((kctlflags & CTL_FLAG_REG_CRIT) == 0) { if ((u_int32_t) space >= datasize) error = 0; else error = ENOBUFS; } else if ((flags & CTL_DATA_CRIT) == 0) { - /* - * Reserve 25% for critical messages - */ - if (space < (sb->sb_hiwat >> 2) || - space < datasize) - error = ENOBUFS; - else - error = 0; + /* + * Reserve 25% for critical messages + */ + if (space < (sb->sb_hiwat >> 2) || + space < datasize) + error = ENOBUFS; + else + error = 0; } else { u_int32_t autorcvbuf_max; @@ -688,10 +740,18 @@ ctl_rcvbspace(struct kctl *kctl, struct socket *so, u_int32_t datasize, if (sb->sb_hiwat > ctl_autorcvbuf_high) ctl_autorcvbuf_high = sb->sb_hiwat; + /* + * A final check + */ + if ((u_int32_t) sbspace(sb) >= datasize) { + error = 0; + } else { + error = ENOBUFS; + } + if (ctl_debug) - printf("%s - grown to %d\n", - __func__, sb->sb_hiwat); - error = 0; + printf("%s - grown to %d error %d\n", + __func__, sb->sb_hiwat, error); } else { error = ENOBUFS; } @@ -703,22 +763,20 @@ ctl_rcvbspace(struct kctl *kctl, struct socket *so, u_int32_t datasize, } errno_t -ctl_enqueuembuf(void *kctlref, u_int32_t unit, struct mbuf *m, u_int32_t flags) +ctl_enqueuembuf(kern_ctl_ref kctlref, u_int32_t unit, struct mbuf *m, + u_int32_t flags) { struct socket *so; errno_t error = 0; - struct kctl *kctl = (struct kctl *)kctlref; int len = m->m_pkthdr.len; + u_int32_t kctlflags; - if (kctl == NULL) - return (EINVAL); - - so = kcb_find_socket(kctl, unit); - - if (so == NULL) + so = kcb_find_socket(kctlref, unit, &kctlflags); + if (so == NULL) { return (EINVAL); + } - if (ctl_rcvbspace(kctl, so, len, flags) != 0) { + if (ctl_rcvbspace(so, len, kctlflags, flags) != 0) { error = ENOBUFS; OSIncrementAtomic64((SInt64 *)&kctlstat.kcs_enqueue_fullsock); goto bye; @@ -768,21 +826,26 @@ ctl_enqueuembuf_list(void *kctlref, u_int32_t unit, struct mbuf *m_list, { struct socket *so = NULL; errno_t error = 0; - struct kctl *kctl = (struct kctl *)kctlref; struct mbuf *m, *nextpkt; int needwakeup = 0; int len; + u_int32_t kctlflags; /* * Need to point the beginning of the list in case of early exit */ m = m_list; - if (kctl == NULL) { + /* + * kcb_find_socket takes the socket lock with a reference + */ + so = kcb_find_socket(kctlref, unit, &kctlflags); + if (so == NULL) { error = EINVAL; goto done; } - if (kctl->flags & CTL_FLAG_REG_SOCK_STREAM) { + + if (kctlflags & CTL_FLAG_REG_SOCK_STREAM) { error = EOPNOTSUPP; goto done; } @@ -790,14 +853,6 @@ ctl_enqueuembuf_list(void *kctlref, u_int32_t unit, struct mbuf *m_list, error = EINVAL; goto done; } - /* - * kcb_find_socket takes the socket lock with a reference - */ - so = kcb_find_socket(kctl, unit); - if (so == NULL) { - error = EINVAL; - goto done; - } for (m = m_list; m != NULL; m = nextpkt) { nextpkt = m->m_nextpkt; @@ -811,7 +866,7 @@ ctl_enqueuembuf_list(void *kctlref, u_int32_t unit, struct mbuf *m_list, * so it's not reliable from a data standpoint */ len = m_space(m); - if (ctl_rcvbspace(kctl, so, len, flags) != 0) { + if (ctl_rcvbspace(so, len, kctlflags, flags) != 0) { error = ENOBUFS; OSIncrementAtomic64( (SInt64 *)&kctlstat.kcs_enqueue_fullsock); @@ -879,19 +934,17 @@ ctl_enqueuedata(void *kctlref, u_int32_t unit, void *data, size_t len, struct socket *so; struct mbuf *m; errno_t error = 0; - struct kctl *kctl = (struct kctl *)kctlref; unsigned int num_needed; struct mbuf *n; size_t curlen = 0; + u_int32_t kctlflags; - if (kctlref == NULL) - return (EINVAL); - - so = kcb_find_socket(kctl, unit); - if (so == NULL) + so = kcb_find_socket(kctlref, unit, &kctlflags); + if (so == NULL) { return (EINVAL); + } - if (ctl_rcvbspace(kctl, so, len, flags) != 0) { + if (ctl_rcvbspace(so, len, kctlflags, flags) != 0) { error = ENOBUFS; OSIncrementAtomic64((SInt64 *)&kctlstat.kcs_enqueue_fullsock); goto bye; @@ -940,20 +993,50 @@ bye: return (error); } +errno_t +ctl_getenqueuepacketcount(kern_ctl_ref kctlref, u_int32_t unit, u_int32_t *pcnt) +{ + struct socket *so; + u_int32_t cnt; + struct mbuf *m1; + + if (pcnt == NULL) + return (EINVAL); + + so = kcb_find_socket(kctlref, unit, NULL); + if (so == NULL) { + return (EINVAL); + } + + cnt = 0; + m1 = so->so_rcv.sb_mb; + while (m1 != NULL) { + if (m1->m_type == MT_DATA || + m1->m_type == MT_HEADER || + m1->m_type == MT_OOBDATA) + cnt += 1; + m1 = m1->m_nextpkt; + } + *pcnt = cnt; + + socket_unlock(so, 1); + + return (0); +} errno_t ctl_getenqueuespace(kern_ctl_ref kctlref, u_int32_t unit, size_t *space) { - struct kctl *kctl = (struct kctl *)kctlref; struct socket *so; long avail; - if (kctlref == NULL || space == NULL) + if (space == NULL) return (EINVAL); - so = kcb_find_socket(kctl, unit); - if (so == NULL) + so = kcb_find_socket(kctlref, unit, NULL); + if (so == NULL) { return (EINVAL); + } avail = sbspace(&so->so_rcv); *space = (avail < 0) ? 0 : avail; @@ -966,15 +1049,15 @@ errno_t ctl_getenqueuereadable(kern_ctl_ref kctlref, u_int32_t unit, u_int32_t *difference) { - struct kctl *kctl = (struct kctl *)kctlref; struct socket *so; - if (kctlref == NULL || difference == NULL) + if (difference == NULL) return (EINVAL); - so = kcb_find_socket(kctl, unit); - if (so == NULL) + so = kcb_find_socket(kctlref, unit, NULL); + if (so == NULL) { return (EINVAL); + } if (so->so_rcv.sb_cc >= so->so_rcv.sb_lowat) { *difference = 0; @@ -1017,16 +1100,13 @@ ctl_ctloutput(struct socket *so, struct sockopt *sopt) if (data == NULL) return (ENOMEM); error = sooptcopyin(sopt, data, - sopt->sopt_valsize, - sopt->sopt_valsize); + sopt->sopt_valsize, sopt->sopt_valsize); } if (error == 0) { socket_unlock(so, 0); - error = (*kctl->setopt)(kcb->kctl, kcb->unit, - kcb->userdata, - sopt->sopt_name, - data, - sopt->sopt_valsize); + error = (*kctl->setopt)(kctl->kctlref, + kcb->unit, kcb->userdata, sopt->sopt_name, + data, sopt->sopt_valsize); socket_lock(so, 0); } FREE(data, M_TEMP); @@ -1050,7 +1130,7 @@ ctl_ctloutput(struct socket *so, struct sockopt *sopt) } len = sopt->sopt_valsize; socket_unlock(so, 0); - error = (*kctl->getopt)(kcb->kctl, kcb->unit, + error = (*kctl->getopt)(kctl->kctlref, kcb->unit, kcb->userdata, sopt->sopt_name, data, &len); if (data != NULL && len > sopt->sopt_valsize) @@ -1126,6 +1206,148 @@ ctl_ioctl(struct socket *so, u_long cmd, caddr_t data, return (error); } +static void +kctl_tbl_grow() +{ + struct kctl **new_table; + uintptr_t new_size; + + lck_mtx_assert(ctl_mtx, LCK_MTX_ASSERT_OWNED); + + while (kctl_tbl_growing) { + /* Another thread is allocating */ + (void) msleep((caddr_t) &kctl_tbl_growing, ctl_mtx, + PSOCK | PCATCH, "kctl_tbl_growing", 0); + } + /* Another thread grew the table */ + if (kctl_table != NULL && kctl_tbl_count < kctl_tbl_size) + return; + + /* Verify we have a sane size */ + if (kctl_tbl_size + KCTL_TBL_INC >= UINT16_MAX) { + printf("%s kctl_tbl_size %lu too big\n", + __func__, kctl_tbl_size); + return; + } + kctl_tbl_growing = 1; + + new_size = kctl_tbl_size + KCTL_TBL_INC; + + lck_mtx_unlock(ctl_mtx); + new_table = _MALLOC(sizeof(struct kctl *) * new_size, + M_TEMP, M_WAIT | M_ZERO); + lck_mtx_lock(ctl_mtx); + + if (new_table != NULL) { + if (kctl_table != NULL) { + bcopy(kctl_table, new_table, + kctl_tbl_size * sizeof(struct kctl *)); + + _FREE(kctl_table, M_TEMP); + } + kctl_table = new_table; + kctl_tbl_size = new_size; + } + + kctl_tbl_growing = 0; +} + +#define KCTLREF_INDEX_MASK 0x0000FFFF +#define KCTLREF_GENCNT_MASK 0xFFFF0000 +#define KCTLREF_GENCNT_SHIFT 16 + +static kern_ctl_ref +kctl_make_ref(struct kctl *kctl) +{ + uintptr_t i; + + lck_mtx_assert(ctl_mtx, LCK_MTX_ASSERT_OWNED); + + if (kctl_tbl_count >= kctl_tbl_size) + kctl_tbl_grow(); + + kctl->kctlref = NULL; + for (i = 0; i < kctl_tbl_size; i++) { + if (kctl_table[i] == NULL) { + uintptr_t ref; + + /* + * Reference is index plus one + */ + kctl_ref_gencnt += 1; + + /* + * Add generation count as salt to reference to prevent + * use after deregister + */ + ref = ((kctl_ref_gencnt << KCTLREF_GENCNT_SHIFT) & + KCTLREF_GENCNT_MASK) + + ((i + 1) & KCTLREF_INDEX_MASK); + + kctl->kctlref = (void *)(ref); + kctl_table[i] = kctl; + kctl_tbl_count++; + break; + } + } + + if (kctl->kctlref == NULL) + panic("%s no space in table", __func__); + + if (ctl_debug > 0) + printf("%s %p for %p\n", + __func__, kctl->kctlref, kctl); + + return (kctl->kctlref); +} + +static void +kctl_delete_ref(kern_ctl_ref kctlref) +{ + /* + * Reference is index plus one + */ + uintptr_t i = (((uintptr_t)kctlref) & KCTLREF_INDEX_MASK) - 1; + + lck_mtx_assert(ctl_mtx, LCK_MTX_ASSERT_OWNED); + + if (i < kctl_tbl_size) { + struct kctl *kctl = kctl_table[i]; + + if (kctl->kctlref == kctlref) { + kctl_table[i] = NULL; + kctl_tbl_count--; + } else { + kctlstat.kcs_bad_kctlref++; + } + } else { + kctlstat.kcs_bad_kctlref++; + } +} + +static struct kctl * +kctl_from_ref(kern_ctl_ref kctlref) +{ + /* + * Reference is index plus one + */ + uintptr_t i = (((uintptr_t)kctlref) & KCTLREF_INDEX_MASK) - 1; + struct kctl *kctl = NULL; + + lck_mtx_assert(ctl_mtx, LCK_MTX_ASSERT_OWNED); + + if (i >= kctl_tbl_size) { + kctlstat.kcs_bad_kctlref++; + return (NULL); + } + kctl = kctl_table[i]; + if (kctl->kctlref != kctlref) { + kctlstat.kcs_bad_kctlref++; + return (NULL); + } + return (kctl); +} + /* * Register/unregister a NKE */ @@ -1153,6 +1375,12 @@ ctl_register(struct kern_ctl_reg *userkctl, kern_ctl_ref *kctlref) lck_mtx_lock(ctl_mtx); + if (kctl_make_ref(kctl) == NULL) { + lck_mtx_unlock(ctl_mtx); + FREE(kctl, M_TEMP); + return (ENOMEM); + } + /* * Kernel Control IDs * @@ -1169,6 +1397,7 @@ ctl_register(struct kern_ctl_reg *userkctl, kern_ctl_ref *kctlref) /* Verify the same name isn't already registered */ if (ctl_find_by_name(userkctl->ctl_name) != NULL) { + kctl_delete_ref(kctl->kctlref); lck_mtx_unlock(ctl_mtx); FREE(kctl, M_TEMP); return (EEXIST); @@ -1212,6 +1441,7 @@ ctl_register(struct kern_ctl_reg *userkctl, kern_ctl_ref *kctlref) } if (ctl_find_by_id_unit(userkctl->ctl_id, userkctl->ctl_unit)) { + kctl_delete_ref(kctl->kctlref); lck_mtx_unlock(ctl_mtx); FREE(kctl, M_TEMP); return (EEXIST); @@ -1263,7 +1493,7 @@ ctl_register(struct kern_ctl_reg *userkctl, kern_ctl_ref *kctlref) lck_mtx_unlock(ctl_mtx); - *kctlref = kctl; + *kctlref = kctl->kctlref; ctl_post_msg(KEV_CTL_REGISTERED, kctl->id); return (0); @@ -1274,18 +1504,16 @@ ctl_deregister(void *kctlref) { struct kctl *kctl; - if (kctlref == NULL) /* sanity check */ - return (EINVAL); - lck_mtx_lock(ctl_mtx); - TAILQ_FOREACH(kctl, &ctl_head, next) { - if (kctl == (struct kctl *)kctlref) - break; - } - if (kctl != (struct kctl *)kctlref) { + if ((kctl = kctl_from_ref(kctlref)) == NULL) { + kctlstat.kcs_bad_kctlref++; lck_mtx_unlock(ctl_mtx); + if (ctl_debug != 0) + printf("%s invalid kctlref %p\n", + __func__, kctlref); return (EINVAL); } + if (!TAILQ_EMPTY(&kctl->kcb_head)) { lck_mtx_unlock(ctl_mtx); return (EBUSY); @@ -1296,6 +1524,7 @@ ctl_deregister(void *kctlref) kctlstat.kcs_reg_count--; kctlstat.kcs_gencnt++; + kctl_delete_ref(kctl->kctlref); lck_mtx_unlock(ctl_mtx); ctl_post_msg(KEV_CTL_DEREGISTERED, kctl->id); @@ -1347,7 +1576,7 @@ ctl_name_by_id(u_int32_t id, char *out_name, size_t maxsize) break; } - if (kctl && kctl->name) { + if (kctl) { if (maxsize > MAX_KCTL_NAME) maxsize = MAX_KCTL_NAME; strlcpy(out_name, kctl->name, maxsize); @@ -1396,48 +1625,66 @@ kcb_find(struct kctl *kctl, u_int32_t unit) } static struct socket * -kcb_find_socket(struct kctl *kctl, u_int32_t unit) +kcb_find_socket(kern_ctl_ref kctlref, u_int32_t unit, u_int32_t *kctlflags) { struct socket *so = NULL; struct ctl_cb *kcb; void *lr_saved; + struct kctl *kctl; + int i; lr_saved = __builtin_return_address(0); lck_mtx_lock(ctl_mtx); - kcb = kcb_find(kctl, unit); - if (kcb && kcb->kctl == kctl) { - so = kcb->so; - if (so) { - kcb->usecount++; - } + /* + * First validate the kctlref + */ + if ((kctl = kctl_from_ref(kctlref)) == NULL) { + kctlstat.kcs_bad_kctlref++; + lck_mtx_unlock(ctl_mtx); + if (ctl_debug != 0) + printf("%s invalid kctlref %p\n", + __func__, kctlref); + return (NULL); } - lck_mtx_unlock(ctl_mtx); - if (so == NULL) { + kcb = kcb_find(kctl, unit); + if (kcb == NULL || kcb->kctl != kctl || (so = kcb->so) == NULL) { + lck_mtx_unlock(ctl_mtx); return (NULL); } + /* + * This prevents the socket from being closed + */ + kcb->usecount++; + /* + * Respect lock ordering: socket before ctl_mtx + */ + lck_mtx_unlock(ctl_mtx); socket_lock(so, 1); + /* + * The socket lock history is more useful if we store + * the address of the caller. + */ + i = (so->next_lock_lr + SO_LCKDBG_MAX - 1) % SO_LCKDBG_MAX; + so->lock_lr[i] = lr_saved; lck_mtx_lock(ctl_mtx); - if (kcb->kctl == NULL) { + + if ((kctl = kctl_from_ref(kctlref)) == NULL || kcb->kctl == NULL) { lck_mtx_unlock(ctl_mtx); socket_unlock(so, 1); so = NULL; lck_mtx_lock(ctl_mtx); - } else { - /* - * The socket lock history is more useful if we store - * the address of the caller. - */ - int i = (so->next_lock_lr + SO_LCKDBG_MAX - 1) % SO_LCKDBG_MAX; - - so->lock_lr[i] = lr_saved; + } else if (kctlflags != NULL) { + *kctlflags = kctl->flags; } + kcb->usecount--; if (kcb->usecount == 0) wakeup((event_t)&kcb->usecount); + lck_mtx_unlock(ctl_mtx); return (so); @@ -1626,7 +1873,7 @@ kctl_reg_list SYSCTL_HANDLER_ARGS xkr->xkr_id = kctl->id; xkr->xkr_reg_unit = kctl->reg_unit; xkr->xkr_flags = kctl->flags; - xkr->xkr_kctlref = (uint64_t)VM_KERNEL_ADDRPERM(kctl); + xkr->xkr_kctlref = (uint64_t)(kctl->kctlref); xkr->xkr_recvbufsize = kctl->recvbufsize; xkr->xkr_sendbufsize = kctl->sendbufsize; xkr->xkr_lastunit = kctl->lastunit; @@ -1808,3 +2055,25 @@ done: lck_mtx_unlock(ctl_mtx); return (error); } + +void +kctl_fill_socketinfo(struct socket *so, struct socket_info *si) +{ + struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb; + struct kern_ctl_info *kcsi = + &si->soi_proto.pri_kern_ctl; + struct kctl *kctl = kcb->kctl; + + si->soi_kind = SOCKINFO_KERN_CTL; + + if (kctl == 0) + return; + + kcsi->kcsi_id = kctl->id; + kcsi->kcsi_reg_unit = kctl->reg_unit; + kcsi->kcsi_flags = kctl->flags; + kcsi->kcsi_recvbufsize = kctl->recvbufsize; + kcsi->kcsi_sendbufsize = kctl->sendbufsize; + kcsi->kcsi_unit = kcb->unit; + strlcpy(kcsi->kcsi_name, kctl->name, MAX_KCTL_NAME); +}