The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/sys_capability.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2008-2011 Robert N. M. Watson
    3  * Copyright (c) 2010-2011 Jonathan Anderson
    4  * Copyright (c) 2012 FreeBSD Foundation
    5  * All rights reserved.
    6  *
    7  * This software was developed at the University of Cambridge Computer
    8  * Laboratory with support from a grant from Google, Inc.
    9  *
   10  * Portions of this software were developed by Pawel Jakub Dawidek under
   11  * sponsorship from the FreeBSD Foundation.
   12  *
   13  * Redistribution and use in source and binary forms, with or without
   14  * modification, are permitted provided that the following conditions
   15  * are met:
   16  * 1. Redistributions of source code must retain the above copyright
   17  *    notice, this list of conditions and the following disclaimer.
   18  * 2. Redistributions in binary form must reproduce the above copyright
   19  *    notice, this list of conditions and the following disclaimer in the
   20  *    documentation and/or other materials provided with the distribution.
   21  *
   22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   32  * SUCH DAMAGE.
   33  */
   34 
   35 /*
   36  * FreeBSD kernel capability facility.
   37  *
   38  * Two kernel features are implemented here: capability mode, a sandboxed mode
   39  * of execution for processes, and capabilities, a refinement on file
   40  * descriptors that allows fine-grained control over operations on the file
   41  * descriptor.  Collectively, these allow processes to run in the style of a
   42  * historic "capability system" in which they can use only resources
   43  * explicitly delegated to them.  This model is enforced by restricting access
   44  * to global namespaces in capability mode.
   45  *
   46  * Capabilities wrap other file descriptor types, binding them to a constant
   47  * rights mask set when the capability is created.  New capabilities may be
   48  * derived from existing capabilities, but only if they have the same or a
   49  * strict subset of the rights on the original capability.
   50  *
   51  * System calls permitted in capability mode are defined in capabilities.conf;
   52  * calls must be carefully audited for safety to ensure that they don't allow
   53  * escape from a sandbox.  Some calls permit only a subset of operations in
   54  * capability mode -- for example, shm_open(2) is limited to creating
   55  * anonymous, rather than named, POSIX shared memory objects.
   56  */
   57 
   58 #include <sys/cdefs.h>
   59 __FBSDID("$FreeBSD: releng/11.0/sys/kern/sys_capability.c 286618 2015-08-11 08:43:50Z ed $");
   60 
   61 #include "opt_capsicum.h"
   62 #include "opt_ktrace.h"
   63 
   64 #include <sys/param.h>
   65 #include <sys/capsicum.h>
   66 #include <sys/file.h>
   67 #include <sys/filedesc.h>
   68 #include <sys/kernel.h>
   69 #include <sys/limits.h>
   70 #include <sys/lock.h>
   71 #include <sys/mutex.h>
   72 #include <sys/proc.h>
   73 #include <sys/syscallsubr.h>
   74 #include <sys/sysproto.h>
   75 #include <sys/sysctl.h>
   76 #include <sys/systm.h>
   77 #include <sys/ucred.h>
   78 #include <sys/uio.h>
   79 #include <sys/ktrace.h>
   80 
   81 #include <security/audit/audit.h>
   82 
   83 #include <vm/uma.h>
   84 #include <vm/vm.h>
   85 
   86 #ifdef CAPABILITY_MODE
   87 
   88 FEATURE(security_capability_mode, "Capsicum Capability Mode");
   89 
   90 /*
   91  * System call to enter capability mode for the process.
   92  */
   93 int
   94 sys_cap_enter(struct thread *td, struct cap_enter_args *uap)
   95 {
   96         struct ucred *newcred, *oldcred;
   97         struct proc *p;
   98 
   99         if (IN_CAPABILITY_MODE(td))
  100                 return (0);
  101 
  102         newcred = crget();
  103         p = td->td_proc;
  104         PROC_LOCK(p);
  105         oldcred = crcopysafe(p, newcred);
  106         newcred->cr_flags |= CRED_FLAG_CAPMODE;
  107         proc_set_cred(p, newcred);
  108         PROC_UNLOCK(p);
  109         crfree(oldcred);
  110         return (0);
  111 }
  112 
  113 /*
  114  * System call to query whether the process is in capability mode.
  115  */
  116 int
  117 sys_cap_getmode(struct thread *td, struct cap_getmode_args *uap)
  118 {
  119         u_int i;
  120 
  121         i = IN_CAPABILITY_MODE(td) ? 1 : 0;
  122         return (copyout(&i, uap->modep, sizeof(i)));
  123 }
  124 
  125 #else /* !CAPABILITY_MODE */
  126 
  127 int
  128 sys_cap_enter(struct thread *td, struct cap_enter_args *uap)
  129 {
  130 
  131         return (ENOSYS);
  132 }
  133 
  134 int
  135 sys_cap_getmode(struct thread *td, struct cap_getmode_args *uap)
  136 {
  137 
  138         return (ENOSYS);
  139 }
  140 
  141 #endif /* CAPABILITY_MODE */
  142 
  143 #ifdef CAPABILITIES
  144 
  145 FEATURE(security_capabilities, "Capsicum Capabilities");
  146 
  147 MALLOC_DECLARE(M_FILECAPS);
  148 
  149 static inline int
  150 _cap_check(const cap_rights_t *havep, const cap_rights_t *needp,
  151     enum ktr_cap_fail_type type)
  152 {
  153         int i;
  154 
  155         for (i = 0; i < nitems(havep->cr_rights); i++) {
  156                 if (!cap_rights_contains(havep, needp)) {
  157 #ifdef KTRACE
  158                         if (KTRPOINT(curthread, KTR_CAPFAIL))
  159                                 ktrcapfail(type, needp, havep);
  160 #endif
  161                         return (ENOTCAPABLE);
  162                 }
  163         }
  164         return (0);
  165 }
  166 
  167 /*
  168  * Test whether a capability grants the requested rights.
  169  */
  170 int
  171 cap_check(const cap_rights_t *havep, const cap_rights_t *needp)
  172 {
  173 
  174         return (_cap_check(havep, needp, CAPFAIL_NOTCAPABLE));
  175 }
  176 
  177 /*
  178  * Convert capability rights into VM access flags.
  179  */
  180 u_char
  181 cap_rights_to_vmprot(cap_rights_t *havep)
  182 {
  183         u_char maxprot;
  184 
  185         maxprot = VM_PROT_NONE;
  186         if (cap_rights_is_set(havep, CAP_MMAP_R))
  187                 maxprot |= VM_PROT_READ;
  188         if (cap_rights_is_set(havep, CAP_MMAP_W))
  189                 maxprot |= VM_PROT_WRITE;
  190         if (cap_rights_is_set(havep, CAP_MMAP_X))
  191                 maxprot |= VM_PROT_EXECUTE;
  192 
  193         return (maxprot);
  194 }
  195 
  196 /*
  197  * Extract rights from a capability for monitoring purposes -- not for use in
  198  * any other way, as we want to keep all capability permission evaluation in
  199  * this one file.
  200  */
  201 
  202 cap_rights_t *
  203 cap_rights_fde(struct filedescent *fde)
  204 {
  205 
  206         return (&fde->fde_rights);
  207 }
  208 
  209 cap_rights_t *
  210 cap_rights(struct filedesc *fdp, int fd)
  211 {
  212 
  213         return (cap_rights_fde(&fdp->fd_ofiles[fd]));
  214 }
  215 
  216 int
  217 kern_cap_rights_limit(struct thread *td, int fd, cap_rights_t *rights)
  218 {
  219         struct filedesc *fdp;
  220         int error;
  221 
  222         fdp = td->td_proc->p_fd;
  223         FILEDESC_XLOCK(fdp);
  224         if (fget_locked(fdp, fd) == NULL) {
  225                 FILEDESC_XUNLOCK(fdp);
  226                 return (EBADF);
  227         }
  228         error = _cap_check(cap_rights(fdp, fd), rights, CAPFAIL_INCREASE);
  229         if (error == 0) {
  230                 fdp->fd_ofiles[fd].fde_rights = *rights;
  231                 if (!cap_rights_is_set(rights, CAP_IOCTL)) {
  232                         free(fdp->fd_ofiles[fd].fde_ioctls, M_FILECAPS);
  233                         fdp->fd_ofiles[fd].fde_ioctls = NULL;
  234                         fdp->fd_ofiles[fd].fde_nioctls = 0;
  235                 }
  236                 if (!cap_rights_is_set(rights, CAP_FCNTL))
  237                         fdp->fd_ofiles[fd].fde_fcntls = 0;
  238         }
  239         FILEDESC_XUNLOCK(fdp);
  240         return (error);
  241 }
  242 
  243 /*
  244  * System call to limit rights of the given capability.
  245  */
  246 int
  247 sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap)
  248 {
  249         cap_rights_t rights;
  250         int error, version;
  251 
  252         cap_rights_init(&rights);
  253 
  254         error = copyin(uap->rightsp, &rights, sizeof(rights.cr_rights[0]));
  255         if (error != 0)
  256                 return (error);
  257         version = CAPVER(&rights);
  258         if (version != CAP_RIGHTS_VERSION_00)
  259                 return (EINVAL);
  260 
  261         error = copyin(uap->rightsp, &rights,
  262             sizeof(rights.cr_rights[0]) * CAPARSIZE(&rights));
  263         if (error != 0)
  264                 return (error);
  265         /* Check for race. */
  266         if (CAPVER(&rights) != version)
  267                 return (EINVAL);
  268 
  269         if (!cap_rights_is_valid(&rights))
  270                 return (EINVAL);
  271 
  272         if (version != CAP_RIGHTS_VERSION) {
  273                 rights.cr_rights[0] &= ~(0x3ULL << 62);
  274                 rights.cr_rights[0] |= ((uint64_t)CAP_RIGHTS_VERSION << 62);
  275         }
  276 #ifdef KTRACE
  277         if (KTRPOINT(td, KTR_STRUCT))
  278                 ktrcaprights(&rights);
  279 #endif
  280 
  281         AUDIT_ARG_FD(uap->fd);
  282         AUDIT_ARG_RIGHTS(&rights);
  283         return (kern_cap_rights_limit(td, uap->fd, &rights));
  284 }
  285 
  286 /*
  287  * System call to query the rights mask associated with a capability.
  288  */
  289 int
  290 sys___cap_rights_get(struct thread *td, struct __cap_rights_get_args *uap)
  291 {
  292         struct filedesc *fdp;
  293         cap_rights_t rights;
  294         int error, fd, i, n;
  295 
  296         if (uap->version != CAP_RIGHTS_VERSION_00)
  297                 return (EINVAL);
  298 
  299         fd = uap->fd;
  300 
  301         AUDIT_ARG_FD(fd);
  302 
  303         fdp = td->td_proc->p_fd;
  304         FILEDESC_SLOCK(fdp);
  305         if (fget_locked(fdp, fd) == NULL) {
  306                 FILEDESC_SUNLOCK(fdp);
  307                 return (EBADF);
  308         }
  309         rights = *cap_rights(fdp, fd);
  310         FILEDESC_SUNLOCK(fdp);
  311         n = uap->version + 2;
  312         if (uap->version != CAPVER(&rights)) {
  313                 /*
  314                  * For older versions we need to check if the descriptor
  315                  * doesn't contain rights not understood by the caller.
  316                  * If it does, we have to return an error.
  317                  */
  318                 for (i = n; i < CAPARSIZE(&rights); i++) {
  319                         if ((rights.cr_rights[i] & ~(0x7FULL << 57)) != 0)
  320                                 return (EINVAL);
  321                 }
  322         }
  323         error = copyout(&rights, uap->rightsp, sizeof(rights.cr_rights[0]) * n);
  324 #ifdef KTRACE
  325         if (error == 0 && KTRPOINT(td, KTR_STRUCT))
  326                 ktrcaprights(&rights);
  327 #endif
  328         return (error);
  329 }
  330 
  331 /*
  332  * Test whether a capability grants the given ioctl command.
  333  * If descriptor doesn't have CAP_IOCTL, then ioctls list is empty and
  334  * ENOTCAPABLE will be returned.
  335  */
  336 int
  337 cap_ioctl_check(struct filedesc *fdp, int fd, u_long cmd)
  338 {
  339         u_long *cmds;
  340         ssize_t ncmds;
  341         long i;
  342 
  343         FILEDESC_LOCK_ASSERT(fdp);
  344         KASSERT(fd >= 0 && fd < fdp->fd_nfiles,
  345             ("%s: invalid fd=%d", __func__, fd));
  346 
  347         ncmds = fdp->fd_ofiles[fd].fde_nioctls;
  348         if (ncmds == -1)
  349                 return (0);
  350 
  351         cmds = fdp->fd_ofiles[fd].fde_ioctls;
  352         for (i = 0; i < ncmds; i++) {
  353                 if (cmds[i] == cmd)
  354                         return (0);
  355         }
  356 
  357         return (ENOTCAPABLE);
  358 }
  359 
  360 /*
  361  * Check if the current ioctls list can be replaced by the new one.
  362  */
  363 static int
  364 cap_ioctl_limit_check(struct filedesc *fdp, int fd, const u_long *cmds,
  365     size_t ncmds)
  366 {
  367         u_long *ocmds;
  368         ssize_t oncmds;
  369         u_long i;
  370         long j;
  371 
  372         oncmds = fdp->fd_ofiles[fd].fde_nioctls;
  373         if (oncmds == -1)
  374                 return (0);
  375         if (oncmds < (ssize_t)ncmds)
  376                 return (ENOTCAPABLE);
  377 
  378         ocmds = fdp->fd_ofiles[fd].fde_ioctls;
  379         for (i = 0; i < ncmds; i++) {
  380                 for (j = 0; j < oncmds; j++) {
  381                         if (cmds[i] == ocmds[j])
  382                                 break;
  383                 }
  384                 if (j == oncmds)
  385                         return (ENOTCAPABLE);
  386         }
  387 
  388         return (0);
  389 }
  390 
  391 int
  392 kern_cap_ioctls_limit(struct thread *td, int fd, u_long *cmds, size_t ncmds)
  393 {
  394         struct filedesc *fdp;
  395         u_long *ocmds;
  396         int error;
  397 
  398         AUDIT_ARG_FD(fd);
  399 
  400         fdp = td->td_proc->p_fd;
  401         FILEDESC_XLOCK(fdp);
  402 
  403         if (fget_locked(fdp, fd) == NULL) {
  404                 error = EBADF;
  405                 goto out;
  406         }
  407 
  408         error = cap_ioctl_limit_check(fdp, fd, cmds, ncmds);
  409         if (error != 0)
  410                 goto out;
  411 
  412         ocmds = fdp->fd_ofiles[fd].fde_ioctls;
  413         fdp->fd_ofiles[fd].fde_ioctls = cmds;
  414         fdp->fd_ofiles[fd].fde_nioctls = ncmds;
  415 
  416         cmds = ocmds;
  417         error = 0;
  418 out:
  419         FILEDESC_XUNLOCK(fdp);
  420         free(cmds, M_FILECAPS);
  421         return (error);
  422 }
  423 
  424 int
  425 sys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap)
  426 {
  427         u_long *cmds;
  428         size_t ncmds;
  429         int error;
  430 
  431         ncmds = uap->ncmds;
  432 
  433         if (ncmds > 256)        /* XXX: Is 256 sane? */
  434                 return (EINVAL);
  435 
  436         if (ncmds == 0) {
  437                 cmds = NULL;
  438         } else {
  439                 cmds = malloc(sizeof(cmds[0]) * ncmds, M_FILECAPS, M_WAITOK);
  440                 error = copyin(uap->cmds, cmds, sizeof(cmds[0]) * ncmds);
  441                 if (error != 0) {
  442                         free(cmds, M_FILECAPS);
  443                         return (error);
  444                 }
  445         }
  446 
  447         return (kern_cap_ioctls_limit(td, uap->fd, cmds, ncmds));
  448 }
  449 
  450 int
  451 sys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap)
  452 {
  453         struct filedesc *fdp;
  454         struct filedescent *fdep;
  455         u_long *cmds;
  456         size_t maxcmds;
  457         int error, fd;
  458 
  459         fd = uap->fd;
  460         cmds = uap->cmds;
  461         maxcmds = uap->maxcmds;
  462 
  463         AUDIT_ARG_FD(fd);
  464 
  465         fdp = td->td_proc->p_fd;
  466         FILEDESC_SLOCK(fdp);
  467 
  468         if (fget_locked(fdp, fd) == NULL) {
  469                 error = EBADF;
  470                 goto out;
  471         }
  472 
  473         /*
  474          * If all ioctls are allowed (fde_nioctls == -1 && fde_ioctls == NULL)
  475          * the only sane thing we can do is to not populate the given array and
  476          * return CAP_IOCTLS_ALL.
  477          */
  478 
  479         fdep = &fdp->fd_ofiles[fd];
  480         if (cmds != NULL && fdep->fde_ioctls != NULL) {
  481                 error = copyout(fdep->fde_ioctls, cmds,
  482                     sizeof(cmds[0]) * MIN(fdep->fde_nioctls, maxcmds));
  483                 if (error != 0)
  484                         goto out;
  485         }
  486         if (fdep->fde_nioctls == -1)
  487                 td->td_retval[0] = CAP_IOCTLS_ALL;
  488         else
  489                 td->td_retval[0] = fdep->fde_nioctls;
  490 
  491         error = 0;
  492 out:
  493         FILEDESC_SUNLOCK(fdp);
  494         return (error);
  495 }
  496 
  497 /*
  498  * Test whether a capability grants the given fcntl command.
  499  */
  500 int
  501 cap_fcntl_check_fde(struct filedescent *fde, int cmd)
  502 {
  503         uint32_t fcntlcap;
  504 
  505         fcntlcap = (1 << cmd);
  506         KASSERT((CAP_FCNTL_ALL & fcntlcap) != 0,
  507             ("Unsupported fcntl=%d.", cmd));
  508 
  509         if ((fde->fde_fcntls & fcntlcap) != 0)
  510                 return (0);
  511 
  512         return (ENOTCAPABLE);
  513 }
  514 
  515 int
  516 cap_fcntl_check(struct filedesc *fdp, int fd, int cmd)
  517 {
  518 
  519         KASSERT(fd >= 0 && fd < fdp->fd_nfiles,
  520             ("%s: invalid fd=%d", __func__, fd));
  521 
  522         return (cap_fcntl_check_fde(&fdp->fd_ofiles[fd], cmd));
  523 }
  524 
  525 int
  526 sys_cap_fcntls_limit(struct thread *td, struct cap_fcntls_limit_args *uap)
  527 {
  528         struct filedesc *fdp;
  529         uint32_t fcntlrights;
  530         int fd;
  531 
  532         fd = uap->fd;
  533         fcntlrights = uap->fcntlrights;
  534 
  535         AUDIT_ARG_FD(fd);
  536         AUDIT_ARG_FCNTL_RIGHTS(fcntlrights);
  537 
  538         if ((fcntlrights & ~CAP_FCNTL_ALL) != 0)
  539                 return (EINVAL);
  540 
  541         fdp = td->td_proc->p_fd;
  542         FILEDESC_XLOCK(fdp);
  543 
  544         if (fget_locked(fdp, fd) == NULL) {
  545                 FILEDESC_XUNLOCK(fdp);
  546                 return (EBADF);
  547         }
  548 
  549         if ((fcntlrights & ~fdp->fd_ofiles[fd].fde_fcntls) != 0) {
  550                 FILEDESC_XUNLOCK(fdp);
  551                 return (ENOTCAPABLE);
  552         }
  553 
  554         fdp->fd_ofiles[fd].fde_fcntls = fcntlrights;
  555         FILEDESC_XUNLOCK(fdp);
  556 
  557         return (0);
  558 }
  559 
  560 int
  561 sys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap)
  562 {
  563         struct filedesc *fdp;
  564         uint32_t rights;
  565         int fd;
  566 
  567         fd = uap->fd;
  568 
  569         AUDIT_ARG_FD(fd);
  570 
  571         fdp = td->td_proc->p_fd;
  572         FILEDESC_SLOCK(fdp);
  573         if (fget_locked(fdp, fd) == NULL) {
  574                 FILEDESC_SUNLOCK(fdp);
  575                 return (EBADF);
  576         }
  577         rights = fdp->fd_ofiles[fd].fde_fcntls;
  578         FILEDESC_SUNLOCK(fdp);
  579 
  580         return (copyout(&rights, uap->fcntlrightsp, sizeof(rights)));
  581 }
  582 
  583 #else /* !CAPABILITIES */
  584 
  585 /*
  586  * Stub Capability functions for when options CAPABILITIES isn't compiled
  587  * into the kernel.
  588  */
  589 
  590 int
  591 sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap)
  592 {
  593 
  594         return (ENOSYS);
  595 }
  596 
  597 int
  598 sys___cap_rights_get(struct thread *td, struct __cap_rights_get_args *uap)
  599 {
  600 
  601         return (ENOSYS);
  602 }
  603 
  604 int
  605 sys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap)
  606 {
  607 
  608         return (ENOSYS);
  609 }
  610 
  611 int
  612 sys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap)
  613 {
  614 
  615         return (ENOSYS);
  616 }
  617 
  618 int
  619 sys_cap_fcntls_limit(struct thread *td, struct cap_fcntls_limit_args *uap)
  620 {
  621 
  622         return (ENOSYS);
  623 }
  624 
  625 int
  626 sys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap)
  627 {
  628 
  629         return (ENOSYS);
  630 }
  631 
  632 #endif /* CAPABILITIES */

Cache object: 350d5b2e973882e2fe50b3bf325ab0d1


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.