On Wed, Apr 17, 2013 at 08:06:43PM -0400, Ted Unangst wrote:

> At some point, every young programmer hears a story about how you can
> use xor to store the pointers of a list. It's a stupid story for many
> reasons, not least of which is the fact that nobody ever does this.
> Sounds like a challenge.
> 
> This diff doesn't actually implement anything like what you're
> probably thinking of when somebody says XORed list pointers. What I've
> done is create an alternate universe version of the SIMPLEQ queue.h
> macros that automatically XORs all the list pointers with a magic
> cookie stored in the list head.
> 
> The reason we do this is because malloc and pool maintain their free
> lists as simple linked lists, with internal pointers. If the naughty
> people discover a use after free bug, this basically gives them a
> write anything to the kernel's memory ticket. That's bad. If we
> encrypt all the pointers, that overwrite suddenly becomes a lot less
> reliable. The cookie is stored in a different part of memory, we have to
> hope it doesn't leak out somehow.
> 
> I'm not the first to think of this, apparently even my phone already
> does something similar. In fact, it may even be doing something
> better, but the approach taken here is drop in simple. Anywhere you
> have a simpleq list, now you can have an xor protected simpleq list.
> 
> If you are concerned about performance, I recommend you upgrade to a
> computer with a hardware accelerated xor instruction.

By the only hardware accelarated operation my computer has is NAND...

        -Ottto

> 
> Index: kern/subr_pool.c
> ===================================================================
> RCS file: /cvs/src/sys/kern/subr_pool.c,v
> retrieving revision 1.118
> diff -u -p -r1.118 subr_pool.c
> --- kern/subr_pool.c  6 Apr 2013 13:41:11 -0000       1.118
> +++ kern/subr_pool.c  15 Apr 2013 13:06:34 -0000
> @@ -67,7 +67,7 @@ struct pool_item_header {
>       /* Page headers */
>       LIST_ENTRY(pool_item_header)
>                               ph_pagelist;    /* pool page list */
> -     SIMPLEQ_HEAD(,pool_item) ph_itemlist;   /* chunk list for this page */
> +     XSIMPLEQ_HEAD(,pool_item) ph_itemlist;  /* chunk list for this page */
>       RB_ENTRY(pool_item_header)
>                               ph_node;        /* Off-page page headers */
>       int                     ph_nmissing;    /* # of chunks in use */
> @@ -80,7 +80,7 @@ struct pool_item_header {
>  struct pool_item {
>       u_int32_t pi_magic;
>       /* Other entries use only this list entry */
> -     SIMPLEQ_ENTRY(pool_item)        pi_list;
> +     XSIMPLEQ_ENTRY(pool_item)       pi_list;
>  };
>  
>  #ifdef POOL_DEBUG
> @@ -620,7 +620,7 @@ startover:
>               /* Start the allocation process over. */
>               goto startover;
>       }
> -     if ((v = pi = SIMPLEQ_FIRST(&ph->ph_itemlist)) == NULL) {
> +     if ((v = pi = XSIMPLEQ_FIRST(&ph->ph_itemlist)) == NULL) {
>               panic("pool_do_get: %s: page empty", pp->pr_wchan);
>       }
>  #ifdef DIAGNOSTIC
> @@ -653,7 +653,7 @@ startover:
>       /*
>        * Remove from item list.
>        */
> -     SIMPLEQ_REMOVE_HEAD(&ph->ph_itemlist, pi_list);
> +     XSIMPLEQ_REMOVE_HEAD(&ph->ph_itemlist, pi_list);
>       pp->pr_nitems--;
>       pp->pr_nout++;
>       if (ph->ph_nmissing == 0) {
> @@ -671,7 +671,7 @@ startover:
>               LIST_INSERT_HEAD(&pp->pr_partpages, ph, ph_pagelist);
>       }
>       ph->ph_nmissing++;
> -     if (SIMPLEQ_EMPTY(&ph->ph_itemlist)) {
> +     if (XSIMPLEQ_EMPTY(&ph->ph_itemlist)) {
>  #ifdef DIAGNOSTIC
>               if (ph->ph_nmissing != pp->pr_itemsperpage) {
>                       panic("pool_do_get: %s: nmissing inconsistent",
> @@ -771,7 +771,7 @@ pool_do_put(struct pool *pp, void *v)
>       }
>  #endif /* DIAGNOSTIC */
>  
> -     SIMPLEQ_INSERT_HEAD(&ph->ph_itemlist, pi, pi_list);
> +     XSIMPLEQ_INSERT_HEAD(&ph->ph_itemlist, pi, pi_list);
>       ph->ph_nmissing--;
>       pp->pr_nitems++;
>       pp->pr_nout--;
> @@ -874,7 +874,7 @@ pool_prime_page(struct pool *pp, caddr_t
>        * Insert page header.
>        */
>       LIST_INSERT_HEAD(&pp->pr_emptypages, ph, ph_pagelist);
> -     SIMPLEQ_INIT(&ph->ph_itemlist);
> +     XSIMPLEQ_INIT(&ph->ph_itemlist);
>       ph->ph_page = storage;
>       ph->ph_pagesize = pp->pr_alloc->pa_pagesz;
>       ph->ph_nmissing = 0;
> @@ -909,7 +909,7 @@ pool_prime_page(struct pool *pp, caddr_t
>               KASSERT(((((vaddr_t)pi) + ioff) & (align - 1)) == 0);
>  
>               /* Insert on page list */
> -             SIMPLEQ_INSERT_TAIL(&ph->ph_itemlist, pi, pi_list);
> +             XSIMPLEQ_INSERT_TAIL(&ph->ph_itemlist, pi, pi_list);
>  
>  #ifdef DIAGNOSTIC
>               pi->pi_magic = poison_value(pi);
> @@ -1130,7 +1130,7 @@ pool_print_pagelist(struct pool_pagelist
>               (*pr)("\t\tpage %p, nmissing %d\n",
>                   ph->ph_page, ph->ph_nmissing);
>  #ifdef DIAGNOSTIC
> -             SIMPLEQ_FOREACH(pi, &ph->ph_itemlist, pi_list) {
> +             XSIMPLEQ_FOREACH(pi, &ph->ph_itemlist, pi_list) {
>                       if (pi->pi_magic != poison_value(pi)) {
>                               (*pr)("\t\t\titem %p, magic 0x%x\n",
>                                   pi, pi->pi_magic);
> @@ -1281,9 +1281,9 @@ pool_chk_page(struct pool *pp, struct po
>               return 1;
>       }
>  
> -     for (pi = SIMPLEQ_FIRST(&ph->ph_itemlist), n = 0;
> +     for (pi = XSIMPLEQ_FIRST(&ph->ph_itemlist), n = 0;
>            pi != NULL;
> -          pi = SIMPLEQ_NEXT(pi,pi_list), n++) {
> +          pi = XSIMPLEQ_NEXT(&ph->ph_itemlist, pi, pi_list), n++) {
>  
>  #ifdef DIAGNOSTIC
>               if (pi->pi_magic != poison_value(pi)) {
> @@ -1379,7 +1379,7 @@ pool_walk(struct pool *pp, int full,
>               n = ph->ph_nmissing;
>  
>               do {
> -                     SIMPLEQ_FOREACH(pi, &ph->ph_itemlist, pi_list) {
> +                     XSIMPLEQ_FOREACH(pi, &ph->ph_itemlist, pi_list) {
>                               if (cp == (caddr_t)pi)
>                                       break;
>                       }
> Index: kern/kern_malloc.c
> ===================================================================
> RCS file: /cvs/src/sys/kern/kern_malloc.c,v
> retrieving revision 1.99
> diff -u -p -r1.99 kern_malloc.c
> --- kern/kern_malloc.c        6 Apr 2013 03:53:25 -0000       1.99
> +++ kern/kern_malloc.c        15 Apr 2013 13:06:34 -0000
> @@ -41,6 +41,8 @@
>  #include <sys/time.h>
>  #include <sys/rwlock.h>
>  
> +#include <dev/rndvar.h>
> +
>  #include <uvm/uvm.h>
>  
>  static __inline__ long BUCKETINDX(size_t sz)
> @@ -132,7 +134,7 @@ struct kmem_freelist {
>       int32_t kf_spare0;
>       int16_t kf_type;
>       int16_t kf_spare1;
> -     SIMPLEQ_ENTRY(kmem_freelist) kf_flist;
> +     XSIMPLEQ_ENTRY(kmem_freelist) kf_flist;
>  };
>  
>  #ifdef DIAGNOSTIC
> @@ -221,7 +223,7 @@ malloc(unsigned long size, int type, int
>               allocsize = round_page(size);
>       else
>               allocsize = 1 << indx;
> -     if (SIMPLEQ_FIRST(&kbp->kb_freelist) == NULL) {
> +     if (XSIMPLEQ_FIRST(&kbp->kb_freelist) == NULL) {
>               npg = atop(round_page(allocsize));
>               va = (caddr_t)uvm_km_kmemalloc_pla(kmem_map, NULL,
>                   (vsize_t)ptoa(npg), 0,
> @@ -273,7 +275,7 @@ malloc(unsigned long size, int type, int
>                       poison_mem(cp, allocsize);
>                       freep->kf_type = M_FREE;
>  #endif /* DIAGNOSTIC */
> -                     SIMPLEQ_INSERT_HEAD(&kbp->kb_freelist, freep, kf_flist);
> +                     XSIMPLEQ_INSERT_HEAD(&kbp->kb_freelist, freep, 
> kf_flist);
>                       if (cp <= va)
>                               break;
>                       cp -= allocsize;
> @@ -283,15 +285,15 @@ malloc(unsigned long size, int type, int
>               freshalloc = 0;
>  #endif
>       }
> -     freep = SIMPLEQ_FIRST(&kbp->kb_freelist);
> -     SIMPLEQ_REMOVE_HEAD(&kbp->kb_freelist, kf_flist);
> +     freep = XSIMPLEQ_FIRST(&kbp->kb_freelist);
> +     XSIMPLEQ_REMOVE_HEAD(&kbp->kb_freelist, kf_flist);
>       va = (caddr_t)freep;
>  #ifdef DIAGNOSTIC
>       savedtype = (unsigned)freep->kf_type < M_LAST ?
>               memname[freep->kf_type] : "???";
> -     if (freshalloc == 0 && SIMPLEQ_FIRST(&kbp->kb_freelist)) {
> +     if (freshalloc == 0 && XSIMPLEQ_FIRST(&kbp->kb_freelist)) {
>               int rv;
> -             vaddr_t addr = (vaddr_t)SIMPLEQ_FIRST(&kbp->kb_freelist);
> +             vaddr_t addr = (vaddr_t)XSIMPLEQ_FIRST(&kbp->kb_freelist);
>  
>               vm_map_lock(kmem_map);
>               rv = uvm_map_checkprot(kmem_map, addr,
> @@ -420,7 +422,7 @@ free(void *addr, int type)
>        */
>       if (freep->kf_spare0 == poison_value(freep)) {
>               struct kmem_freelist *fp;
> -             SIMPLEQ_FOREACH(fp, &kbp->kb_freelist, kf_flist) {
> +             XSIMPLEQ_FOREACH(fp, &kbp->kb_freelist, kf_flist) {
>                       if (addr != fp)
>                               continue;
>                       printf("multiply freed item %p\n", addr);
> @@ -453,7 +455,7 @@ free(void *addr, int type)
>               wakeup(ksp);
>       ksp->ks_inuse--;
>  #endif
> -     SIMPLEQ_INSERT_TAIL(&kbp->kb_freelist, freep, kf_flist);
> +     XSIMPLEQ_INSERT_TAIL(&kbp->kb_freelist, freep, kf_flist);
>       splx(s);
>  }
>  
> @@ -538,7 +540,7 @@ kmeminit(void)
>       kmemusage = (struct kmemusage *) uvm_km_zalloc(kernel_map,
>               (vsize_t)(nkmempages * sizeof(struct kmemusage)));
>       for (indx = 0; indx < MINBUCKET + 16; indx++) {
> -             SIMPLEQ_INIT(&bucket[indx].kb_freelist);
> +             XSIMPLEQ_INIT(&bucket[indx].kb_freelist);
>       }
>  #ifdef KMEMSTATS
>       for (indx = 0; indx < MINBUCKET + 16; indx++) {
> Index: sys/malloc.h
> ===================================================================
> RCS file: /cvs/src/sys/sys/malloc.h,v
> retrieving revision 1.104
> diff -u -p -r1.104 malloc.h
> --- sys/malloc.h      6 Apr 2013 03:53:25 -0000       1.104
> +++ sys/malloc.h      15 Apr 2013 13:06:34 -0000
> @@ -347,7 +347,7 @@ struct kmem_freelist;
>   * Set of buckets for each size of memory block that is retained
>   */
>  struct kmembuckets {
> -     SIMPLEQ_HEAD(, kmem_freelist) kb_freelist; /* list of free blocks */
> +     XSIMPLEQ_HEAD(, kmem_freelist) kb_freelist; /* list of free blocks */
>       u_int64_t kb_calls;     /* total calls to allocate this size */
>       u_int64_t kb_total;     /* total number of blocks allocated */
>       u_int64_t kb_totalfree; /* # of free elements in this bucket */
> Index: sys/queue.h
> ===================================================================
> RCS file: /cvs/src/sys/sys/queue.h,v
> retrieving revision 1.36
> diff -u -p -r1.36 queue.h
> --- sys/queue.h       11 Apr 2012 13:29:14 -0000      1.36
> +++ sys/queue.h       15 Apr 2013 13:06:34 -0000
> @@ -317,6 +317,84 @@ struct {                                                 
>         \
>  } while (0)
>  
>  /*
> + * XOR Simple queue definitions.
> + */
> +#define XSIMPLEQ_HEAD(name, type)                                    \
> +struct name {                                                                
> \
> +     struct type *sqx_first; /* first element */                     \
> +     struct type **sqx_last; /* addr of last next element */         \
> +     unsigned long sqx_cookie;                                       \
> +}
> +
> +#define XSIMPLEQ_ENTRY(type)                                         \
> +struct {                                                             \
> +     struct type *sqx_next;  /* next element */                      \
> +}
> +
> +/*
> + * Simple queue access methods.
> + */
> +#define XSIMPLEQ_XOR(head, ptr)          ((typeof(ptr))((head)->sqx_cookie ^ 
> (unsigned long)(ptr)))
> +#define      XSIMPLEQ_FIRST(head)        XSIMPLEQ_XOR(head, 
> ((head)->sqx_first))
> +#define      XSIMPLEQ_END(head)          NULL
> +#define      XSIMPLEQ_EMPTY(head)        (XSIMPLEQ_FIRST(head) == 
> XSIMPLEQ_END(head))
> +#define      XSIMPLEQ_NEXT(head, elm, field)    XSIMPLEQ_XOR(head, 
> ((elm)->field.sqx_next))
> +
> +
> +#define XSIMPLEQ_FOREACH(var, head, field)                           \
> +     for ((var) = XSIMPLEQ_FIRST(head);                              \
> +         (var) != XSIMPLEQ_END(head);                                \
> +         (var) = XSIMPLEQ_NEXT(head, var, field))
> +
> +#define      XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar)                   
> \
> +     for ((var) = XSIMPLEQ_FIRST(head);                              \
> +         (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1);     \
> +         (var) = (tvar))
> +
> +/*
> + * Simple queue functions.
> + */
> +#define      XSIMPLEQ_INIT(head) do {                                        
> \
> +     (head)->sqx_cookie = arc4random();                              \
> +     if (sizeof(unsigned long) == 8)                                 \
> +             (head)->sqx_cookie |= ((unsigned long)arc4random()) << 
> sizeof(long) * 4;        \
> +     (head)->sqx_first = XSIMPLEQ_XOR(head, NULL);                   \
> +     (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first);      \
> +} while (0)
> +
> +#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do {                  \
> +     if (((elm)->field.sqx_next = (head)->sqx_first) == XSIMPLEQ_XOR(head, 
> NULL))    \
> +             (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next);  
> \
> +     (head)->sqx_first = XSIMPLEQ_XOR(head, (elm));                  \
> +} while (0)
> +
> +#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do {                  \
> +     (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL);               \
> +     *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm));    
> \
> +     (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next);  \
> +} while (0)
> +
> +#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {                
> \
> +     if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == 
> XSIMPLEQ_XOR(head, NULL)) \
> +             (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next);  
> \
> +     (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm));          \
> +} while (0)
> +
> +#define XSIMPLEQ_REMOVE_HEAD(head, field) do {                       \
> +     if (((head)->sqx_first = XSIMPLEQ_XOR(head, 
> (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \
> +             (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first);      
> \
> +} while (0)
> +
> +#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do {                 \
> +     if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head,         \
> +         (elm)->field.sqx_next)->field.sqx_next)                     \
> +         == XSIMPLEQ_XOR(head, NULL))                                \
> +             (head)->sqx_last =                                      \
> +                 XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
> +} while (0)
> +
> +                 
> +/*
>   * Tail queue definitions.
>   */
>  #define TAILQ_HEAD(name, type)                                               
> \
> 

Reply via email to