Browse Source

ieee1394: raw1394: prevent unloading of low-level driver

Unloading the low-level driver module of a FireWire host can lead to
all sorts of trouble if a raw1394 userspace client is using the host.
Just disallow it by incrementing the LLD's module reference count on
a RAW1394_REQ_SET_CARD write operation.  Decrement it when the file
is closed.

This feature wouldn't be relevant if "modprobe -r video1394" or
"modprobe -r dv1394" didn't automatically unload ohci1394 too.
http://bugzilla.kernel.org/show_bug.cgi?id=7701

Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Signed-off-by: Dan Dennedy <dan@dennedy.org>
Stefan Richter 18 years ago
parent
commit
0fe4c6fcac
1 changed files with 29 additions and 19 deletions
  1. 29 19
      drivers/ieee1394/raw1394.c

+ 29 - 19
drivers/ieee1394/raw1394.c

@@ -636,27 +636,32 @@ static int state_initialized(struct file_info *fi, struct pending_request *req)
 
 
 	case RAW1394_REQ_SET_CARD:
 	case RAW1394_REQ_SET_CARD:
 		spin_lock_irqsave(&host_info_lock, flags);
 		spin_lock_irqsave(&host_info_lock, flags);
-		if (req->req.misc < host_count) {
-			list_for_each_entry(hi, &host_info_list, list) {
-				if (!req->req.misc--)
-					break;
-			}
-			get_device(&hi->host->device);	// XXX Need to handle failure case
-			list_add_tail(&fi->list, &hi->file_info_list);
-			fi->host = hi->host;
-			fi->state = connected;
-
-			req->req.error = RAW1394_ERROR_NONE;
-			req->req.generation = get_hpsb_generation(fi->host);
-			req->req.misc = (fi->host->node_id << 16)
-			    | fi->host->node_count;
-			if (fi->protocol_version > 3) {
-				req->req.misc |=
-				    NODEID_TO_NODE(fi->host->irm_id) << 8;
-			}
-		} else {
+		if (req->req.misc >= host_count) {
 			req->req.error = RAW1394_ERROR_INVALID_ARG;
 			req->req.error = RAW1394_ERROR_INVALID_ARG;
+			goto out_set_card;
 		}
 		}
+		list_for_each_entry(hi, &host_info_list, list)
+			if (!req->req.misc--)
+				break;
+		get_device(&hi->host->device); /* FIXME handle failure case */
+		list_add_tail(&fi->list, &hi->file_info_list);
+
+		/* prevent unloading of the host's low-level driver */
+		if (!try_module_get(hi->host->driver->owner)) {
+			req->req.error = RAW1394_ERROR_ABORTED;
+			goto out_set_card;
+		}
+		WARN_ON(fi->host);
+		fi->host = hi->host;
+		fi->state = connected;
+
+		req->req.error = RAW1394_ERROR_NONE;
+		req->req.generation = get_hpsb_generation(fi->host);
+		req->req.misc = (fi->host->node_id << 16)
+				| fi->host->node_count;
+		if (fi->protocol_version > 3)
+			req->req.misc |= NODEID_TO_NODE(fi->host->irm_id) << 8;
+out_set_card:
 		spin_unlock_irqrestore(&host_info_lock, flags);
 		spin_unlock_irqrestore(&host_info_lock, flags);
 
 
 		req->req.length = 0;
 		req->req.length = 0;
@@ -2955,6 +2960,11 @@ static int raw1394_release(struct inode *inode, struct file *file)
 		put_device(&fi->host->device);
 		put_device(&fi->host->device);
 	}
 	}
 
 
+	spin_lock_irqsave(&host_info_lock, flags);
+	if (fi->host)
+		module_put(fi->host->driver->owner);
+	spin_unlock_irqrestore(&host_info_lock, flags);
+
 	kfree(fi);
 	kfree(fi);
 
 
 	return 0;
 	return 0;