⬢ DragonFlyBSD Kernel Audit
DF-5688 / fix.diff
← back to finding ↓ download raw
--- a/sys/libkern/linux_idr.c
+++ b/sys/libkern/linux_idr.c
@@ -184,7 +184,7 @@
 	if (id < 0 || id >= idp->idr_count)
 		return (NULL);
 	idrnp = &idp->idr_nodes[id];
-	if (idrnp->allocated == 0)
+	if (idrnp->used == 0)
 		return (NULL);
 	return (idrnp);
 }
@@ -345,6 +345,7 @@
 	*id = resid;
 	idr_reserve(idp, resid, 1);
 	idp->idr_nodes[resid].data = ptr;
+	idp->idr_nodes[resid].used = 1;
 
 	lwkt_reltoken(&idp->idr_token);
 	return (0);
@@ -400,6 +401,7 @@
 	result = id;
 	idr_reserve(idp, id, 1);
 	idp->idr_nodes[id].data = ptr;
+	idp->idr_nodes[id].used = 1;
 
 done:
 	lwkt_reltoken(&idp->idr_token);
@@ -470,12 +472,13 @@
 		lwkt_reltoken(&idp->idr_token);
 		return NULL;
 	}
-        if (idp->idr_nodes[id].allocated == 0) {
+        if (idp->idr_nodes[id].used == 0) {
                 lwkt_reltoken(&idp->idr_token);
                 return NULL;
         }
 	ptr = idp->idr_nodes[id].data;
 	idp->idr_nodes[id].data = NULL;
+	idp->idr_nodes[id].used = 0;
 	idr_reserve(idp, id, -1);
 	idrfixup(idp, id);
 	lwkt_reltoken(&idp->idr_token);
@@ -517,14 +520,29 @@
 {
 	void *ret;
 
+	lwkt_gettoken(&idp->idr_token);
+	ret = __idr_find(idp, id);
+	lwkt_reltoken(&idp->idr_token);
+	return (ret);
+}
+
+/*
+ * Unlocked variant for callers already holding idp->idr_token
+ * (idr_get_next() in the drm linux-compat shim).
+ */
+void *
+__idr_find(struct idr *idp, int id)
+{
+	void *ret;
+
 	if (id < 0 || id >= idp->idr_count) {
 		ret = NULL;
-	} else if (idp->idr_nodes[id].allocated == 0) {
+	} else if (idp->idr_nodes[id].used == 0) {
 		ret = NULL;
 	} else {
 		ret = idp->idr_nodes[id].data;
 	}
-	return ret;
+	return (ret);
 }
 
 int
@@ -534,14 +552,16 @@
 	int i, error = 0;
 	struct idr_node *nodes;
 
+	lwkt_gettoken(&idp->idr_token);
 	nodes = idp->idr_nodes;
 	for (i = 0; i < idp->idr_count; i++) {
-		if (nodes[i].data != NULL && nodes[i].allocated > 0) {
+		if (nodes[i].data != NULL && nodes[i].used > 0) {
 			error = fn(i, nodes[i].data, data);
 			if (error != 0)
 				break;
 		}
 	}
+	lwkt_reltoken(&idp->idr_token);
 	return error;
 }
 
--- a/sys/sys/idr.h
+++ b/sys/sys/idr.h
@@ -53,6 +53,7 @@
 struct idr_node {
 	void	*data;
 	int	 allocated;
+	int	 used;			/* leaf id itself is allocated */
 };
 
 struct idr {
@@ -76,6 +77,7 @@
 int	 idr_pre_get(struct idr *idp, unsigned gfp_mask);
 
 void	 idr_init(struct idr *idp);
+void	*__idr_find(struct idr *idp, int id);
 
 int idr_alloc(struct idr *idp, void *ptr, int start, int end, unsigned gfp_mask);
 
--- a/sys/dev/drm/include/linux/idr.h
+++ b/sys/dev/drm/include/linux/idr.h
@@ -109,7 +109,7 @@
 	lwkt_gettoken(&idp->idr_token);
 
 	for (int id = *nextid; id < idp->idr_count; id++) {
-		res = idr_find(idp, id);
+		res = __idr_find(idp, id);
 		if (res == NULL)
 			continue;
 		*nextid = id;