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/kern_pax.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 /* $NetBSD: kern_pax.c,v 1.8.2.2 2007/07/09 10:30:56 liamjfoy Exp $ */
    2 
    3 /*-
    4  * Copyright (c) 2006 Elad Efrat <elad@NetBSD.org>
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  * 3. All advertising materials mentioning features or use of this software
   16  *    must display the following acknowledgement:
   17  *      This product includes software developed by Elad Efrat.
   18  * 4. The name of the author may not be used to endorse or promote products
   19  *    derived from this software without specific prior written permission.
   20  *
   21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   31  */
   32 
   33 #include "opt_pax.h"
   34 
   35 #include <sys/param.h>
   36 #include <sys/proc.h>
   37 #include <sys/exec_elf.h>
   38 #include <sys/pax.h>
   39 #include <sys/sysctl.h>
   40 #include <sys/malloc.h>
   41 #include <sys/fileassoc.h>
   42 #include <sys/syslog.h>
   43 #include <sys/vnode.h>
   44 #include <sys/queue.h>
   45 #include <sys/kauth.h>
   46 
   47 #ifdef PAX_MPROTECT
   48 static int pax_mprotect_enabled = 1;
   49 static int pax_mprotect_global = PAX_MPROTECT;
   50 
   51 specificdata_key_t pax_mprotect_key;
   52 #endif
   53 
   54 #ifdef PAX_SEGVGUARD
   55 #ifndef PAX_SEGVGUARD_EXPIRY
   56 #define PAX_SEGVGUARD_EXPIRY            (2 * 60)
   57 #endif
   58 
   59 #ifndef PAX_SEGVGUARD_SUSPENSION
   60 #define PAX_SEGVGUARD_SUSPENSION        (10 * 60)
   61 #endif
   62 
   63 #ifndef PAX_SEGVGUARD_MAXCRASHES
   64 #define PAX_SEGVGUARD_MAXCRASHES        5
   65 #endif
   66 
   67 static int pax_segvguard_enabled = 1;
   68 static int pax_segvguard_global = PAX_SEGVGUARD;
   69 static int pax_segvguard_expiry = PAX_SEGVGUARD_EXPIRY;
   70 static int pax_segvguard_suspension = PAX_SEGVGUARD_SUSPENSION;
   71 static int pax_segvguard_maxcrashes = PAX_SEGVGUARD_MAXCRASHES;
   72 
   73 static fileassoc_t segvguard_id;
   74 specificdata_key_t pax_segvguard_key;
   75 
   76 struct pax_segvguard_uid_entry {
   77         uid_t sue_uid;
   78         size_t sue_ncrashes;
   79         time_t sue_expiry;
   80         time_t sue_suspended;
   81         LIST_ENTRY(pax_segvguard_uid_entry) sue_list;
   82 };
   83 
   84 struct pax_segvguard_entry {
   85         LIST_HEAD(, pax_segvguard_uid_entry) segv_uids;
   86 };
   87 #endif /* PAX_SEGVGUARD */
   88 
   89 /* PaX internal setspecific flags */
   90 #define PAX_MPROTECT_EXPLICIT_ENABLE    (void *)0x01
   91 #define PAX_MPROTECT_EXPLICIT_DISABLE   (void *)0x02
   92 #define PAX_SEGVGUARD_EXPLICIT_ENABLE   (void *)0x03
   93 #define PAX_SEGVGUARD_EXPLICIT_DISABLE  (void *)0x04
   94 
   95 SYSCTL_SETUP(sysctl_security_pax_setup, "sysctl security.pax setup")
   96 {
   97         const struct sysctlnode *rnode = NULL, *cnode;
   98 
   99         sysctl_createv(clog, 0, NULL, &rnode,
  100                        CTLFLAG_PERMANENT,
  101                        CTLTYPE_NODE, "security", NULL,
  102                        NULL, 0, NULL, 0,
  103                        CTL_SECURITY, CTL_EOL);
  104 
  105         sysctl_createv(clog, 0, &rnode, &rnode,
  106                        CTLFLAG_PERMANENT,
  107                        CTLTYPE_NODE, "pax",
  108                        SYSCTL_DESCR("PaX (exploit mitigation) features."),
  109                        NULL, 0, NULL, 0,
  110                        CTL_CREATE, CTL_EOL);
  111 
  112         cnode = rnode;
  113 
  114 #ifdef PAX_MPROTECT
  115         sysctl_createv(clog, 0, &rnode, &rnode,
  116                        CTLFLAG_PERMANENT,
  117                        CTLTYPE_NODE, "mprotect",
  118                        SYSCTL_DESCR("mprotect(2) W^X restrictions."),
  119                        NULL, 0, NULL, 0,
  120                        CTL_CREATE, CTL_EOL);
  121         sysctl_createv(clog, 0, &rnode, NULL,
  122                        CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
  123                        CTLTYPE_INT, "enabled",
  124                        SYSCTL_DESCR("Restrictions enabled."),
  125                        NULL, 0, &pax_mprotect_enabled, 0,
  126                        CTL_CREATE, CTL_EOL);
  127         sysctl_createv(clog, 0, &rnode, NULL,
  128                        CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
  129                        CTLTYPE_INT, "global",
  130                        SYSCTL_DESCR("When enabled, unless explicitly "
  131                                     "specified, apply restrictions to "
  132                                     "all processes."),
  133                        NULL, 0, &pax_mprotect_global, 0,
  134                        CTL_CREATE, CTL_EOL);
  135 #endif /* PAX_MPROTECT */
  136 
  137         rnode = cnode;
  138 
  139 #ifdef PAX_SEGVGUARD
  140         sysctl_createv(clog, 0, &rnode, &rnode,
  141                        CTLFLAG_PERMANENT,
  142                        CTLTYPE_NODE, "segvguard",
  143                        SYSCTL_DESCR("PaX segvguard."),
  144                        NULL, 0, NULL, 0,
  145                        CTL_CREATE, CTL_EOL);
  146         sysctl_createv(clog, 0, &rnode, NULL,
  147                        CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
  148                        CTLTYPE_INT, "enabled",
  149                        SYSCTL_DESCR("segvguard enabled."),
  150                        NULL, 0, &pax_segvguard_enabled, 0,
  151                        CTL_CREATE, CTL_EOL);
  152         sysctl_createv(clog, 0, &rnode, NULL,
  153                        CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
  154                        CTLTYPE_INT, "global",
  155                        SYSCTL_DESCR("segvguard all programs."),
  156                        NULL, 0, &pax_segvguard_global, 0,
  157                        CTL_CREATE, CTL_EOL);
  158         sysctl_createv(clog, 0, &rnode, NULL,
  159                        CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
  160                        CTLTYPE_INT, "expiry_timeout",
  161                        SYSCTL_DESCR("Entry expiry timeout (in seconds)."),
  162                        NULL, 0, &pax_segvguard_expiry, 0,
  163                        CTL_CREATE, CTL_EOL);
  164         sysctl_createv(clog, 0, &rnode, NULL,
  165                        CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
  166                        CTLTYPE_INT, "suspend_timeout",
  167                        SYSCTL_DESCR("Entry suspension timeout (in seconds)."),
  168                        NULL, 0, &pax_segvguard_suspension, 0,
  169                        CTL_CREATE, CTL_EOL);
  170         sysctl_createv(clog, 0, &rnode, NULL,
  171                        CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
  172                        CTLTYPE_INT, "max_crashes",
  173                        SYSCTL_DESCR("Max number of crashes before expiry."),
  174                        NULL, 0, &pax_segvguard_maxcrashes, 0,
  175                        CTL_CREATE, CTL_EOL);
  176 #endif /* PAX_SEGVGUARD */
  177 }
  178 
  179 /*
  180  * Initialize PaX.
  181  */
  182 void
  183 pax_init(void)
  184 {
  185 #ifdef PAX_MPROTECT
  186         proc_specific_key_create(&pax_mprotect_key, NULL);
  187 #endif /* PAX_MPROTECT */
  188 
  189 #ifdef PAX_SEGVGUARD
  190         segvguard_id = fileassoc_register("segvguard", pax_segvguard_cb);
  191         proc_specific_key_create(&pax_segvguard_key, NULL);
  192 #endif /* PAX_SEGVGUARD */
  193 }
  194 
  195 void
  196 pax_adjust(struct lwp *l, uint32_t f)
  197 {
  198 #ifdef PAX_MPROTECT
  199         if (pax_mprotect_enabled) {
  200                 if (f & ELF_NOTE_PAX_MPROTECT)
  201                         proc_setspecific(l->l_proc, pax_mprotect_key,
  202                             PAX_MPROTECT_EXPLICIT_ENABLE);
  203                 if (f & ELF_NOTE_PAX_NOMPROTECT)
  204                         proc_setspecific(l->l_proc, pax_mprotect_key,
  205                             PAX_MPROTECT_EXPLICIT_DISABLE);
  206         }
  207 #endif /* PAX_MPROTECT */
  208 
  209 #ifdef PAX_SEGVGUARD
  210         if (pax_segvguard_enabled) {
  211                 if (f & ELF_NOTE_PAX_GUARD)
  212                         proc_setspecific(l->l_proc, pax_segvguard_key,
  213                             PAX_SEGVGUARD_EXPLICIT_ENABLE);
  214                 if (f & ELF_NOTE_PAX_NOGUARD)
  215                         proc_setspecific(l->l_proc, pax_segvguard_key,
  216                             PAX_SEGVGUARD_EXPLICIT_DISABLE);
  217         }
  218 #endif /* PAX_SEGVGUARD */
  219 }
  220 
  221 #ifdef PAX_MPROTECT
  222 void
  223 pax_mprotect(struct lwp *l, vm_prot_t *prot, vm_prot_t *maxprot)
  224 {
  225         void *t;
  226 
  227         if (!pax_mprotect_enabled)
  228                 return;
  229 
  230         t = proc_getspecific(l->l_proc, pax_mprotect_key);
  231         if ((pax_mprotect_global && t == PAX_MPROTECT_EXPLICIT_DISABLE) ||
  232             (!pax_mprotect_global && t != PAX_MPROTECT_EXPLICIT_ENABLE))
  233                 return;
  234 
  235         if ((*prot & (VM_PROT_WRITE|VM_PROT_EXECUTE)) != VM_PROT_EXECUTE) {
  236                 *prot &= ~VM_PROT_EXECUTE;
  237                 *maxprot &= ~VM_PROT_EXECUTE;
  238         } else {
  239                 *prot &= ~VM_PROT_WRITE;
  240                 *maxprot &= ~VM_PROT_WRITE;
  241         }
  242 }
  243 #endif /* PAX_MPROTECT */
  244 
  245 #ifdef PAX_SEGVGUARD
  246 void
  247 pax_segvguard_cb(void *v, int what)
  248 {
  249         struct pax_segvguard_entry *p;
  250         struct pax_segvguard_uid_entry *up;
  251 
  252         if (v == NULL)
  253                 return;
  254 
  255         if (what == FILEASSOC_CLEANUP_FILE) {
  256                 p = v;
  257                 while ((up = LIST_FIRST(&p->segv_uids)) != NULL) {
  258                         LIST_REMOVE(up, sue_list);
  259                         free(up, M_TEMP);
  260                 }
  261         }
  262 
  263         free(v, M_TEMP);
  264 }
  265 
  266 /*
  267  * Called when a process of image vp generated a segfault.
  268  */
  269 int
  270 pax_segvguard(struct lwp *l, struct vnode *vp, const char *name,
  271     boolean_t crashed)
  272 {
  273         struct pax_segvguard_entry *p;
  274         struct pax_segvguard_uid_entry *up;
  275         struct timeval tv;
  276         uid_t uid;
  277         void *t;
  278         boolean_t have_uid;
  279 
  280         if (!pax_segvguard_enabled)
  281                 return (0);
  282 
  283         t = proc_getspecific(l->l_proc, pax_segvguard_key);
  284         if ((pax_segvguard_global && t == PAX_SEGVGUARD_EXPLICIT_DISABLE) ||
  285             (!pax_segvguard_global && t != PAX_SEGVGUARD_EXPLICIT_ENABLE))
  286                 return (0);
  287 
  288         if (segvguard_id == FILEASSOC_INVAL || vp == NULL)
  289                 return (EFAULT);        
  290 
  291         /* Check if we already monitor the file. */
  292         p = fileassoc_lookup(vp, segvguard_id);
  293 
  294         /* Fast-path if starting a program we don't know. */
  295         if (p == NULL && !crashed)
  296                 return (0);
  297 
  298         microtime(&tv);
  299 
  300         /*
  301          * If a program we don't know crashed, we need to create a new entry
  302          * for it.
  303          */
  304         if (p == NULL) {
  305                 p = malloc(sizeof(*p), M_TEMP, M_WAITOK);
  306                 if (fileassoc_add(vp, segvguard_id, p) != 0) {
  307                         fileassoc_table_add(vp->v_mount, 16);
  308                         fileassoc_add(vp, segvguard_id, p);
  309                 }
  310                 LIST_INIT(&p->segv_uids);
  311 
  312                 /*
  313                  * Initialize a new entry with "crashes so far" of 1.
  314                  * The expiry time is when we purge the entry if it didn't
  315                  * reach the limit.
  316                  */
  317                 up = malloc(sizeof(*up), M_TEMP, M_WAITOK);
  318                 up->sue_uid = kauth_cred_getuid(l->l_cred);
  319                 up->sue_ncrashes = 1;
  320                 up->sue_expiry = tv.tv_sec + pax_segvguard_expiry;
  321                 up->sue_suspended = 0;
  322 
  323                 LIST_INSERT_HEAD(&p->segv_uids, up, sue_list);
  324 
  325                 return (0);
  326         }
  327 
  328         /*
  329          * A program we "know" either executed or crashed again.
  330          * See if it's a culprit we're familiar with.
  331          */
  332         uid = kauth_cred_getuid(l->l_cred);
  333         have_uid = FALSE;
  334         LIST_FOREACH(up, &p->segv_uids, sue_list) {
  335                 if (up->sue_uid == uid) {
  336                         have_uid = TRUE;
  337                         break;
  338                 }
  339         }
  340 
  341         /*
  342          * It's someone else. Add an entry for him if we crashed.
  343          */
  344         if (!have_uid) {
  345                 if (crashed) {
  346                         up = malloc(sizeof(*up), M_TEMP, M_WAITOK);
  347                         up->sue_uid = uid;
  348                         up->sue_ncrashes = 1;
  349                         up->sue_expiry = tv.tv_sec + pax_segvguard_expiry;
  350                         up->sue_suspended = 0;
  351 
  352                         LIST_INSERT_HEAD(&p->segv_uids, up, sue_list);
  353                 }
  354 
  355                 return (0);
  356         }
  357 
  358         if (crashed) {
  359                 /* Check if timer on previous crashes expired first. */
  360                 if (up->sue_expiry < tv.tv_sec) {
  361                         log(LOG_INFO, "PaX Segvguard: [%s] Suspension"
  362                             " expired.\n", name ? name : "unknown");
  363 
  364                         up->sue_ncrashes = 1;
  365                         up->sue_expiry = tv.tv_sec + pax_segvguard_expiry;
  366                         up->sue_suspended = 0;
  367 
  368                         return (0);
  369                 }
  370 
  371                 up->sue_ncrashes++;
  372 
  373                 if (up->sue_ncrashes >= pax_segvguard_maxcrashes) {
  374                         log(LOG_ALERT, "PaX Segvguard: [%s] Suspending "
  375                             "execution for %d seconds after %zu crashes.\n",
  376                             name ? name : "unknown", pax_segvguard_suspension,
  377                             up->sue_ncrashes);
  378 
  379                         /* Suspend this program for a while. */
  380                         up->sue_suspended = tv.tv_sec + pax_segvguard_suspension;
  381                         up->sue_ncrashes = 0;
  382                         up->sue_expiry = 0;
  383                 }
  384         } else {
  385                 /* Are we supposed to be suspended? */
  386                 if (up->sue_suspended > tv.tv_sec) {
  387                         log(LOG_ALERT, "PaX Segvguard: [%s] Preventing "
  388                             "execution due to repeated segfaults.\n", name ?
  389                             name : "unknown");
  390 
  391                         return (EPERM);
  392                 }
  393         }
  394 
  395         return (0);
  396 }
  397 #endif /* PAX_SEGVGUARD */

Cache object: dd19d4cde56526de07964c60bff9c5e2


[ 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.