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
This commit is contained in:
parent
7c4342890b
commit
e266a0f7f0
@ -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.
|
||||
*/
|
||||
@ -1066,12 +1110,6 @@ kern_kldload(struct thread *td, const char *file, int *fileid)
|
||||
if ((error = priv_check(td, PRIV_KLD_LOAD)) != 0)
|
||||
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
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
/*
|
||||
|
Loading…
x
Reference in New Issue
Block a user