1 /* $NetBSD: kern_uidinfo.c,v 1.13 2021/12/28 13:28:24 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 1982, 1986, 1991, 1993 5 * The Regents of the University of California. All rights reserved. 6 * (c) UNIX System Laboratories, Inc. 7 * All or some portions of this file are derived from material licensed 8 * to the University of California by American Telephone and Telegraph 9 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 10 * the permission of UNIX System Laboratories, Inc. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: kern_uidinfo.c,v 1.13 2021/12/28 13:28:24 riastradh Exp $"); 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/kmem.h> 43 #include <sys/proc.h> 44 #include <sys/atomic.h> 45 #include <sys/uidinfo.h> 46 #include <sys/sysctl.h> 47 #include <sys/kauth.h> 48 #include <sys/cpu.h> 49 50 static SLIST_HEAD(uihashhead, uidinfo) *uihashtbl; 51 static u_long uihash; 52 53 #define UIHASH(uid) (&uihashtbl[(uid) & uihash]) 54 55 static int 56 sysctl_kern_uidinfo_cnt(SYSCTLFN_ARGS) 57 { 58 static const struct { 59 const char *name; 60 u_int value; 61 } nv[] = { 62 #define _MEM(n) { # n, offsetof(struct uidinfo, ui_ ## n) } 63 _MEM(proccnt), 64 _MEM(lwpcnt), 65 _MEM(lockcnt), 66 _MEM(semcnt), 67 _MEM(sbsize), 68 #undef _MEM 69 }; 70 71 for (size_t i = 0; i < __arraycount(nv); i++) 72 if (strcmp(nv[i].name, rnode->sysctl_name) == 0) { 73 uint64_t cnt; 74 struct sysctlnode node = *rnode; 75 struct uidinfo *uip; 76 77 node.sysctl_data = &cnt; 78 uip = uid_find(kauth_cred_geteuid(l->l_cred)); 79 80 *(uint64_t *)node.sysctl_data = 81 *(u_long *)((char *)uip + nv[i].value); 82 83 return sysctl_lookup(SYSCTLFN_CALL(&node)); 84 } 85 86 return EINVAL; 87 } 88 89 static struct sysctllog *kern_uidinfo_sysctllog; 90 91 static void 92 sysctl_kern_uidinfo_setup(void) 93 { 94 const struct sysctlnode *rnode, *cnode; 95 96 sysctl_createv(&kern_uidinfo_sysctllog, 0, NULL, &rnode, 97 CTLFLAG_PERMANENT, 98 CTLTYPE_NODE, "uidinfo", 99 SYSCTL_DESCR("Resource usage per uid"), 100 NULL, 0, NULL, 0, 101 CTL_KERN, CTL_CREATE, CTL_EOL); 102 103 sysctl_createv(&kern_uidinfo_sysctllog, 0, &rnode, &cnode, 104 CTLFLAG_PERMANENT, 105 CTLTYPE_QUAD, "proccnt", 106 SYSCTL_DESCR("Number of processes for the current user"), 107 sysctl_kern_uidinfo_cnt, 0, NULL, 0, 108 CTL_CREATE, CTL_EOL); 109 sysctl_createv(&kern_uidinfo_sysctllog, 0, &rnode, &cnode, 110 CTLFLAG_PERMANENT, 111 CTLTYPE_QUAD, "lwpcnt", 112 SYSCTL_DESCR("Number of lwps for the current user"), 113 sysctl_kern_uidinfo_cnt, 0, NULL, 0, 114 CTL_CREATE, CTL_EOL); 115 sysctl_createv(&kern_uidinfo_sysctllog, 0, &rnode, &cnode, 116 CTLFLAG_PERMANENT, 117 CTLTYPE_QUAD, "lockcnt", 118 SYSCTL_DESCR("Number of locks for the current user"), 119 sysctl_kern_uidinfo_cnt, 0, NULL, 0, 120 CTL_CREATE, CTL_EOL); 121 sysctl_createv(&kern_uidinfo_sysctllog, 0, &rnode, &cnode, 122 CTLFLAG_PERMANENT, 123 CTLTYPE_QUAD, "semcnt", 124 SYSCTL_DESCR("Number of semaphores used for the current user"), 125 sysctl_kern_uidinfo_cnt, 0, NULL, 0, 126 CTL_CREATE, CTL_EOL); 127 sysctl_createv(&kern_uidinfo_sysctllog, 0, &rnode, &cnode, 128 CTLFLAG_PERMANENT, 129 CTLTYPE_QUAD, "sbsize", 130 SYSCTL_DESCR("Socket buffers used for the current user"), 131 sysctl_kern_uidinfo_cnt, 0, NULL, 0, 132 CTL_CREATE, CTL_EOL); 133 } 134 135 static int 136 uid_stats(struct hashstat_sysctl *hs, bool fill) 137 { 138 struct uidinfo *uip; 139 uint64_t chain; 140 141 strlcpy(hs->hash_name, "uihash", sizeof(hs->hash_name)); 142 strlcpy(hs->hash_desc, "user info (uid->used proc) hash", 143 sizeof(hs->hash_desc)); 144 if (!fill) 145 return 0; 146 147 hs->hash_size = uihash + 1; 148 149 for (size_t i = 0; i < hs->hash_size; i++) { 150 chain = 0; 151 SLIST_FOREACH(uip, &uihashtbl[i], ui_hash) { 152 membar_datadep_consumer(); 153 chain++; 154 } 155 if (chain > 0) { 156 hs->hash_used++; 157 hs->hash_items += chain; 158 if (chain > hs->hash_maxchain) 159 hs->hash_maxchain = chain; 160 } 161 } 162 163 return 0; 164 } 165 166 void 167 uid_init(void) 168 { 169 170 /* 171 * In case of MP system, SLIST_FOREACH would force a cache line 172 * write-back for every modified 'uidinfo', thus we try to keep the 173 * lists short. 174 */ 175 const u_int uihash_sz = (maxcpus > 1 ? 1024 : 64); 176 177 uihashtbl = hashinit(uihash_sz, HASH_SLIST, true, &uihash); 178 179 /* 180 * Ensure that uid 0 is always in the user hash table, as 181 * sbreserve() expects it available from interrupt context. 182 */ 183 (void)uid_find(0); 184 sysctl_kern_uidinfo_setup(); 185 hashstat_register("uihash", uid_stats); 186 } 187 188 struct uidinfo * 189 uid_find(uid_t uid) 190 { 191 struct uidinfo *uip, *uip_first, *newuip; 192 struct uihashhead *uipp; 193 194 uipp = UIHASH(uid); 195 newuip = NULL; 196 197 /* 198 * To make insertion atomic, abstraction of SLIST will be violated. 199 */ 200 uip_first = uipp->slh_first; 201 again: 202 SLIST_FOREACH(uip, uipp, ui_hash) { 203 membar_datadep_consumer(); 204 if (uip->ui_uid != uid) 205 continue; 206 if (newuip != NULL) 207 kmem_free(newuip, sizeof(*newuip)); 208 return uip; 209 } 210 if (newuip == NULL) 211 newuip = kmem_zalloc(sizeof(*newuip), KM_SLEEP); 212 newuip->ui_uid = uid; 213 214 /* 215 * If atomic insert is unsuccessful, another thread might be 216 * allocated this 'uid', thus full re-check is needed. 217 */ 218 newuip->ui_hash.sle_next = uip_first; 219 membar_producer(); 220 uip = atomic_cas_ptr(&uipp->slh_first, uip_first, newuip); 221 if (uip != uip_first) { 222 uip_first = uip; 223 goto again; 224 } 225 226 return newuip; 227 } 228 229 /* 230 * Change the count associated with number of processes 231 * a given user is using. 232 */ 233 int 234 chgproccnt(uid_t uid, int diff) 235 { 236 struct uidinfo *uip; 237 long proccnt; 238 239 uip = uid_find(uid); 240 proccnt = atomic_add_long_nv(&uip->ui_proccnt, diff); 241 KASSERTMSG(proccnt >= 0, "uid=%d diff=%d proccnt=%ld", 242 uid, diff, proccnt); 243 return proccnt; 244 } 245 246 /* 247 * Change the count associated with number of lwps 248 * a given user is using. 249 */ 250 int 251 chglwpcnt(uid_t uid, int diff) 252 { 253 struct uidinfo *uip; 254 long lwpcnt; 255 256 uip = uid_find(uid); 257 lwpcnt = atomic_add_long_nv(&uip->ui_lwpcnt, diff); 258 KASSERTMSG(lwpcnt >= 0, "uid=%d diff=%d lwpcnt=%ld", 259 uid, diff, lwpcnt); 260 return lwpcnt; 261 } 262 263 /* 264 * Change the count associated with number of semaphores 265 * a given user is using. 266 */ 267 int 268 chgsemcnt(uid_t uid, int diff) 269 { 270 struct uidinfo *uip; 271 long semcnt; 272 273 uip = uid_find(uid); 274 semcnt = atomic_add_long_nv(&uip->ui_semcnt, diff); 275 KASSERTMSG(semcnt >= 0, "uid=%d diff=%d semcnt=%ld", 276 uid, diff, semcnt); 277 return semcnt; 278 } 279 280 int 281 chgsbsize(struct uidinfo *uip, u_long *hiwat, u_long to, rlim_t xmax) 282 { 283 rlim_t nsb; 284 const long diff = to - *hiwat; 285 286 nsb = (rlim_t)atomic_add_long_nv((long *)&uip->ui_sbsize, diff); 287 if (diff > 0 && nsb > xmax) { 288 atomic_add_long((long *)&uip->ui_sbsize, -diff); 289 return 0; 290 } 291 *hiwat = to; 292 return 1; 293 } 294