diff options
Diffstat (limited to 'sys/cam/cam_periph.c')
-rw-r--r-- | sys/cam/cam_periph.c | 26 |
1 files changed, 23 insertions, 3 deletions
diff --git a/sys/cam/cam_periph.c b/sys/cam/cam_periph.c index 5bab8fb..247ebb8 100644 --- a/sys/cam/cam_periph.c +++ b/sys/cam/cam_periph.c @@ -534,7 +534,7 @@ cam_periph_unlock(struct cam_periph *periph) int cam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) { - int numbufs, i; + int numbufs, i, j; int flags[CAM_PERIPH_MAXMAPS]; u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; u_int32_t lengths[CAM_PERIPH_MAXMAPS]; @@ -659,8 +659,28 @@ cam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) /* set the direction */ mapinfo->bp[i]->b_iocmd = flags[i]; - /* map the buffer into kernel memory */ - vmapbuf(mapinfo->bp[i]); + /* + * Map the buffer into kernel memory. + * + * Note that useracc() alone is not a sufficient test. + * vmapbuf() can still fail due to a smaller file mapped + * into a larger area of VM, or if userland races against + * vmapbuf() after the useracc() check. + */ + if (vmapbuf(mapinfo->bp[i]) < 0) { + printf("cam_periph_mapmem: error, " + "address %p, length %lu isn't " + "user accessible any more\n", + (void *)*data_ptrs[i], + (u_long)lengths[i]); + for (j = 0; j < i; ++j) { + *data_ptrs[j] = mapinfo->bp[j]->b_saveaddr; + mapinfo->bp[j]->b_flags &= ~B_PHYS; + relpbuf(mapinfo->bp[j], NULL); + } + PRELE(curproc); + return(EACCES); + } /* set our pointer to the new mapped area */ *data_ptrs[i] = mapinfo->bp[i]->b_data; |