⬢ DragonFlyBSD Kernel Audit
DF-4987 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/raid/tws/tws_user.c
+++ b/sys/dev/raid/tws/tws_user.c
@@ -97,10 +97,18 @@ tws_passthru(struct tws_softc *sc, void *buf)
     int error;
     u_int16_t lun4;
 
     if ( tws_get_state(sc) == TWS_RESET ) {
         return(EBUSY);
     }
+
+    /*
+     * The data tag (tws.c:518-533) is built for TWS_MAX_IO_SIZE (128 kB)
+     * with 46 segments.  Larger lengths make bus_dmamap_load() deliver
+     * EFBIG (command is still submitted with a truncated SGL) or return
+     * EINPROGRESS, which panics the kernel for this BUS_DMA_PROTECTED tag
+     * ("protected dmamap callback will be defered").  Reject up front.
+     */
+    if ( ubuf->driver_pkt.buffer_length > TWS_MAX_IO_SIZE )
+        return(EINVAL);
 
     do {
         req = tws_get_request(sc, TWS_PASSTHRU_REQ);
         if ( !req ) {
             sc->chan = 1;
@@ -117,11 +125,19 @@ tws_passthru(struct tws_softc *sc, void *buf)
     req->length = ubuf->driver_pkt.buffer_length;
     TWS_TRACE_DEBUG(sc, "datal,rid", req->length, req->request_id);
     if ( req->length ) {
         req->data = kmalloc(req->length, M_TWS, M_WAITOK | M_ZERO);
-        error = copyin(ubuf->pdata, req->data, req->length);
+        if ( req->data == NULL ) {
+            req->state = TWS_REQ_STATE_FREE;
+            if ( sc->chan ) { sc->chan = 0; wakeup((void *)&sc->chan); }
+            return(ENOMEM);
+        }
+        error = copyin(ubuf->pdata, req->data, req->length);
+        if ( error ) {
+            kfree(req->data, M_TWS);
+            req->data = NULL;
+            req->state = TWS_REQ_STATE_FREE;
+            if ( sc->chan ) { sc->chan = 0; wakeup((void *)&sc->chan); }
+            return(error);   /* do NOT submit the command with zeroed data */
+        }
     }