--- a/sys/bus/cam/cam_extend.c +++ b/sys/bus/cam/cam_extend.c @@ -27,15 +27,43 @@ #include #include #include +#include #include "cam_extend.h" +/* + * The pointer arrays are shared between periph attach/detach (writers: + * cam_extend_set growth kmalloc/bcopy/kfree of ea->ps, and + * cam_extend_release's NULL store) and every periph open/strategy/ + * ioctl/close path (readers: cam_extend_get), with no CAM-level lock + * coupling the two sides -- e.g. scsi_pt.c's ptctor drops the sim lock + * around its cam_extend_set() call. Growth therefore races lockless + * readers and can hand them a pointer read out of just-freed kernel + * memory. Serialize all access with one leaf lock. + */ +static struct lock cam_extend_lk; + +static void +cam_extend_lockinit(void *arg) +{ + lockinit(&cam_extend_lk, "camext", 0, 0); +} +SYSINIT(camextlk, SI_SUB_DRIVERS, SI_ORDER_FIRST, cam_extend_lockinit, NULL); + struct extend_array { int nelem; void **ps; }; +static void * +cam_extend_get_locked(struct extend_array *ea, int index) +{ + if (ea == NULL || index >= ea->nelem || index < 0) + return NULL; + return ea->ps[index]; +} + /* EXTEND_CHUNK: Number of extend slots to allocate whenever we need a new * one. */ @@ -53,6 +81,9 @@ void * cam_extend_set(struct extend_array *ea, int index, void *value) { + void *ret; + + lockmgr(&cam_extend_lk, LK_EXCLUSIVE); if (index >= ea->nelem) { void **space; space = kmalloc(sizeof(void *) * (index + EXTEND_CHUNK), @@ -69,28 +100,33 @@ } if (ea->ps[index]) { kprintf("extend_set: entry %d already has storage.\n", index); - return 0; - } - else + ret = 0; + } else { ea->ps[index] = value; + ret = value; + } + lockmgr(&cam_extend_lk, LK_RELEASE); - return value; + return (ret); } void * cam_extend_get(struct extend_array *ea, int index) { - if (ea == NULL || index >= ea->nelem || index < 0) - return NULL; - return ea->ps[index]; + void *p; + + lockmgr(&cam_extend_lk, LK_SHARED); + p = cam_extend_get_locked(ea, index); + lockmgr(&cam_extend_lk, LK_RELEASE); + return (p); } void cam_extend_release(struct extend_array *ea, int index) { - void *p = cam_extend_get(ea, index); - if (p) { + lockmgr(&cam_extend_lk, LK_EXCLUSIVE); + if (cam_extend_get_locked(ea, index) != NULL) ea->ps[index] = NULL; - } + lockmgr(&cam_extend_lk, LK_RELEASE); }