DF-5662 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | --- sys/bus/cam/cam_extend.c.orig +++ sys/bus/cam/cam_extend.c @@ -4,9 +4,9 @@ * * TRW Financial Systems, in accordance with their agreement with Carnegie * Mellon University, makes this software available to CMU to distribute - * or use in any manner that they see fit as long as this message is kept with - * the software. For this reason TFS also grants any other persons or - * organisations permission to use or modify this software. + * or use in any manner that they see fit as long as this message is kept + * with the software. For this reason TFS also grants any other persons + * or organisations permission to use or modify this software. * * TFS supplies this software to be publicly redistributed * on the understanding that TFS is not responsible for the correct @@ -27,6 +27,8 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/malloc.h> +#include <sys/spinlock2.h> +#include <sys/limits.h> #include "cam_extend.h" @@ -34,6 +36,7 @@ { int nelem; void **ps; + struct spinlock ex_lock; }; /* EXTEND_CHUNK: Number of extend slots to allocate whenever we need a new @@ -46,51 +49,99 @@ struct extend_array * cam_extend_new(void) { - return(kmalloc(sizeof(struct extend_array), M_DEVBUF, - M_INTWAIT | M_ZERO)); + struct extend_array *ea; + + ea = kmalloc(sizeof(struct extend_array), M_DEVBUF, + M_INTWAIT | M_ZERO); + spin_init(&ea->ex_lock, "cam_extend"); + return (ea); } +/* + * Returns the stored value on success, NULL on failure (bad index, + * allocation failure, or the slot already has storage). + * + * NOTE: The index is fully validated. The old code only checked the + * upper bound, so any negative index indexed before the array + * (an out-of-bounds kernel write) or faulted on a NULL ps. + * + * The array is also synchronized now: growth frees the old ps[] + * chunk, which racing lockless cam_extend_get() readers could + * still dereference (use-after-free read handed back as the + * stored object pointer). + */ void * cam_extend_set(struct extend_array *ea, int index, void *value) { + void **space, **oldps; + + if (index < 0 || index > INT_MAX - EXTEND_CHUNK) + return (NULL); + if (index >= ea->nelem) { - void **space; space = kmalloc(sizeof(void *) * (index + EXTEND_CHUNK), M_DEVBUF, M_INTWAIT | M_ZERO); + if (space == NULL) + return (NULL); - /* Make sure we have something to copy before we copy it */ - if (ea->nelem) { - bcopy(ea->ps, space, sizeof(void *) * ea->nelem); - kfree(ea->ps, M_DEVBUF); + /* + * Re-check under the lock; the allocation was done + * unlocked because M_INTWAIT may block. + */ + spin_lock(&ea->ex_lock); + if (index >= ea->nelem) { + /* Make sure we have something to copy before we + * copy it */ + if (ea->nelem) { + bcopy(ea->ps, space, + sizeof(void *) * ea->nelem); + } + oldps = ea->ps; + ea->ps = space; + ea->nelem = index + EXTEND_CHUNK; + spin_unlock(&ea->ex_lock); + if (oldps != NULL) + kfree(oldps, M_DEVBUF); + } else { + spin_unlock(&ea->ex_lock); + kfree(space, M_DEVBUF); } - - ea->ps = space; - ea->nelem = index + EXTEND_CHUNK; } + + spin_lock(&ea->ex_lock); if (ea->ps[index]) { + spin_unlock(&ea->ex_lock); kprintf("extend_set: entry %d already has storage.\n", index); - return 0; + return (NULL); } - else - ea->ps[index] = value; + ea->ps[index] = value; + spin_unlock(&ea->ex_lock); - return value; + return (value); } void * cam_extend_get(struct extend_array *ea, int index) { + void *p; + if (ea == NULL || index >= ea->nelem || index < 0) return NULL; - return ea->ps[index]; + spin_lock(&ea->ex_lock); + p = ea->ps[index]; + spin_unlock(&ea->ex_lock); + return (p); } void cam_extend_release(struct extend_array *ea, int index) { - void *p = cam_extend_get(ea, index); - if (p) { + if (ea == NULL || index >= ea->nelem || index < 0) + return; + spin_lock(&ea->ex_lock); + if (ea->ps[index] != NULL) { ea->ps[index] = NULL; } + spin_unlock(&ea->ex_lock); } |