1 /* $NetBSD: kern_uidinfo.c,v 1.12 2021/04/01 06:25:45 simonb 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.12 2021/04/01 06:25:45 simonb 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 KASSERT(proccnt >= 0); 242 return proccnt; 243 } 244 245 /* 246 * Change the count associated with number of lwps 247 * a given user is using. 248 */ 249 int 250 chglwpcnt(uid_t uid, int diff) 251 { 252 struct uidinfo *uip; 253 long lwpcnt; 254 255 uip = uid_find(uid); 256 lwpcnt = atomic_add_long_nv(&uip->ui_lwpcnt, diff); 257 KASSERT(lwpcnt >= 0); 258 return lwpcnt; 259 } 260 261 /* 262 * Change the count associated with number of semaphores 263 * a given user is using. 264 */ 265 int 266 chgsemcnt(uid_t uid, int diff) 267 { 268 struct uidinfo *uip; 269 long semcnt; 270 271 uip = uid_find(uid); 272 semcnt = atomic_add_long_nv(&uip->ui_semcnt, diff); 273 KASSERT(semcnt >= 0); 274 return semcnt; 275 } 276 277 int 278 chgsbsize(struct uidinfo *uip, u_long *hiwat, u_long to, rlim_t xmax) 279 { 280 rlim_t nsb; 281 const long diff = to - *hiwat; 282 283 nsb = (rlim_t)atomic_add_long_nv((long *)&uip->ui_sbsize, diff); 284 if (diff > 0 && nsb > xmax) { 285 atomic_add_long((long *)&uip->ui_sbsize, -diff); 286 return 0; 287 } 288 *hiwat = to; 289 return 1; 290 } 291