aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2021-05-20 14:50:43 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2021-05-31 15:09:22 +0000
commite266a0f7f001c7886eab56d8c058d92d87010400 (patch)
treebaba5fb978cf9669cc14d2c477fc0b4b13a4aad8
parent7c4342890bf17b72f0d79ada1326d9cbf34e736c (diff)
downloadsrc-e266a0f7f001c7886eab56d8c058d92d87010400.tar.gz
src-e266a0f7f001c7886eab56d8c058d92d87010400.zip
kern linker: do not allow more than one kldload and kldunload syscalls simultaneously
kld_sx is dropped e.g. for executing sysinits, which allows user to initiate kldunload while module is not yet fully initialized. Reviewed by: markj Differential revision: https://reviews.freebsd.org/D30456 Sponsored by: The FreeBSD Foundation MFC after: 1 week
-rw-r--r--sys/kern/kern_linker.c86
-rw-r--r--sys/sys/linker.h9
2 files changed, 77 insertions, 18 deletions
diff --git a/sys/kern/kern_linker.c b/sys/kern/kern_linker.c
index d30db0fb2dba..bb17c020e98a 100644
--- a/sys/kern/kern_linker.c
+++ b/sys/kern/kern_linker.c
@@ -106,6 +106,7 @@ MALLOC_DEFINE(M_LINKER, "linker", "kernel linker");
linker_file_t linker_kernel_file;
static struct sx kld_sx; /* kernel linker lock */
+static bool kld_busy;
/*
* Load counter used by clients to determine if a linker file has been
@@ -1050,6 +1051,49 @@ linker_search_symbol_name(caddr_t value, char *buf, u_int buflen,
M_WAITOK));
}
+int
+linker_kldload_busy(int flags)
+{
+ int error;
+
+ MPASS((flags & ~(LINKER_UB_UNLOCK | LINKER_UB_LOCKED |
+ LINKER_UB_PCATCH)) == 0);
+ if ((flags & LINKER_UB_LOCKED) != 0)
+ sx_assert(&kld_sx, SA_XLOCKED);
+
+ if ((flags & LINKER_UB_LOCKED) == 0)
+ sx_xlock(&kld_sx);
+ while (kld_busy) {
+ error = sx_sleep(&kld_busy, &kld_sx,
+ (flags & LINKER_UB_PCATCH) != 0 ? PCATCH : 0,
+ "kldbusy", 0);
+ if (error != 0) {
+ if ((flags & LINKER_UB_UNLOCK) != 0)
+ sx_xunlock(&kld_sx);
+ return (error);
+ }
+ }
+ kld_busy = true;
+ if ((flags & LINKER_UB_UNLOCK) != 0)
+ sx_xunlock(&kld_sx);
+ return (0);
+}
+
+void
+linker_kldload_unbusy(int flags)
+{
+ MPASS((flags & ~LINKER_UB_LOCKED) == 0);
+ if ((flags & LINKER_UB_LOCKED) != 0)
+ sx_assert(&kld_sx, SA_XLOCKED);
+
+ if ((flags & LINKER_UB_LOCKED) == 0)
+ sx_xlock(&kld_sx);
+ MPASS(kld_busy);
+ kld_busy = false;
+ wakeup(&kld_busy);
+ sx_xunlock(&kld_sx);
+}
+
/*
* Syscalls.
*/
@@ -1067,12 +1111,6 @@ kern_kldload(struct thread *td, const char *file, int *fileid)
return (error);
/*
- * It is possible that kldloaded module will attach a new ifnet,
- * so vnet context must be set when this ocurs.
- */
- CURVNET_SET(TD_TO_VNET(td));
-
- /*
* If file does not contain a qualified name or any dot in it
* (kldname.ko, or kldname.ver.ko) treat it as an interface
* name.
@@ -1085,19 +1123,27 @@ kern_kldload(struct thread *td, const char *file, int *fileid)
modname = file;
}
- sx_xlock(&kld_sx);
- error = linker_load_module(kldname, modname, NULL, NULL, &lf);
- if (error) {
+ error = linker_kldload_busy(LINKER_UB_PCATCH);
+ if (error != 0) {
sx_xunlock(&kld_sx);
- goto done;
+ return (error);
}
- lf->userrefs++;
- if (fileid != NULL)
- *fileid = lf->id;
- sx_xunlock(&kld_sx);
-done:
+ /*
+ * It is possible that kldloaded module will attach a new ifnet,
+ * so vnet context must be set when this ocurs.
+ */
+ CURVNET_SET(TD_TO_VNET(td));
+
+ error = linker_load_module(kldname, modname, NULL, NULL, &lf);
CURVNET_RESTORE();
+
+ if (error == 0) {
+ lf->userrefs++;
+ if (fileid != NULL)
+ *fileid = lf->id;
+ }
+ linker_kldload_unbusy(LINKER_UB_LOCKED);
return (error);
}
@@ -1132,8 +1178,13 @@ kern_kldunload(struct thread *td, int fileid, int flags)
if ((error = priv_check(td, PRIV_KLD_UNLOAD)) != 0)
return (error);
+ error = linker_kldload_busy(LINKER_UB_PCATCH);
+ if (error != 0) {
+ sx_xunlock(&kld_sx);
+ return (error);
+ }
+
CURVNET_SET(TD_TO_VNET(td));
- sx_xlock(&kld_sx);
lf = linker_find_file_by_id(fileid);
if (lf) {
KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs));
@@ -1153,9 +1204,8 @@ kern_kldunload(struct thread *td, int fileid, int flags)
}
} else
error = ENOENT;
- sx_xunlock(&kld_sx);
-
CURVNET_RESTORE();
+ linker_kldload_unbusy(LINKER_UB_LOCKED);
return (error);
}
diff --git a/sys/sys/linker.h b/sys/sys/linker.h
index 73eea35dab55..a3d5eb0e72cf 100644
--- a/sys/sys/linker.h
+++ b/sys/sys/linker.h
@@ -196,6 +196,15 @@ int linker_search_symbol_name(caddr_t value, char *buf, u_int buflen,
/* HWPMC helper */
void *linker_hwpmc_list_objects(void);
+/* kldload/kldunload syscalls blocking */
+#define LINKER_UB_UNLOCK 0x0001 /* busy: unlock kld_sx locked on
+ return */
+#define LINKER_UB_LOCKED 0x0002 /* busy/unbusy: kld_sx locked on
+ entry */
+#define LINKER_UB_PCATCH 0x0004 /* busy: sleep interruptible */
+int linker_kldload_busy(int flags);
+void linker_kldload_unbusy(int flags);
+
#endif /* _KERNEL */
/*