sysctl-s in a module should be accessible only when the module is initialized
A sysctl can have a custom handler that may access data that is initialized via SYSINIT(9) or via a module event handler (also invoked via SYSINIT). Thus, it is not safe to allow access to the module's sysctl-s until the initialization is performed. Likewise, we should not allow access to teh sysctl-s after the module is uninitialized. The latter is easy to achieve by properly ordering linker_file_unregister_sysctls and linker_file_sysuninit. The former is not as easy for two reasons: - the initialization may depend on tunables which get set when sysctl-s are registered, so we need to set the tunables before running sysinit-s - the initialization may try to dynamically add more sysctl-s under statically defined sysctl nodes So, this change splits the sysctl setup into two phases. In the first phase the sysctl-s are registered as before but they are disabled and hidden from consumers. In the second phase, done after sysinit-s, normal access to the sysctl-s is enabled. The change should affect only dynamic module loading and unloading after the system boot-up. Nothing changes for sysctl-s compiled into the kernel and sysctl-s in preloaded modules. Discussed with: hselasky, ian, jhb Reviewed by: julian, kib MFC after: 2 weeks Sponsored by: Panzura Differential Revision: https://reviews.freebsd.org/D12545
This commit is contained in:
parent
287c718f32
commit
693593b6f0
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=324311
@ -288,7 +288,7 @@ linker_file_sysuninit(linker_file_t lf)
|
||||
}
|
||||
|
||||
static void
|
||||
linker_file_register_sysctls(linker_file_t lf)
|
||||
linker_file_register_sysctls(linker_file_t lf, bool enable)
|
||||
{
|
||||
struct sysctl_oid **start, **stop, **oidp;
|
||||
|
||||
@ -298,13 +298,39 @@ linker_file_register_sysctls(linker_file_t lf)
|
||||
|
||||
sx_assert(&kld_sx, SA_XLOCKED);
|
||||
|
||||
if (linker_file_lookup_set(lf, "sysctl_set", &start, &stop, NULL) != 0)
|
||||
return;
|
||||
|
||||
sx_xunlock(&kld_sx);
|
||||
sysctl_wlock();
|
||||
for (oidp = start; oidp < stop; oidp++) {
|
||||
if (enable)
|
||||
sysctl_register_oid(*oidp);
|
||||
else
|
||||
sysctl_register_disabled_oid(*oidp);
|
||||
}
|
||||
sysctl_wunlock();
|
||||
sx_xlock(&kld_sx);
|
||||
}
|
||||
|
||||
static void
|
||||
linker_file_enable_sysctls(linker_file_t lf)
|
||||
{
|
||||
struct sysctl_oid **start, **stop, **oidp;
|
||||
|
||||
KLD_DPF(FILE,
|
||||
("linker_file_enable_sysctls: enable SYSCTLs for %s\n",
|
||||
lf->filename));
|
||||
|
||||
sx_assert(&kld_sx, SA_XLOCKED);
|
||||
|
||||
if (linker_file_lookup_set(lf, "sysctl_set", &start, &stop, NULL) != 0)
|
||||
return;
|
||||
|
||||
sx_xunlock(&kld_sx);
|
||||
sysctl_wlock();
|
||||
for (oidp = start; oidp < stop; oidp++)
|
||||
sysctl_register_oid(*oidp);
|
||||
sysctl_enable_oid(*oidp);
|
||||
sysctl_wunlock();
|
||||
sx_xlock(&kld_sx);
|
||||
}
|
||||
@ -430,7 +456,7 @@ linker_load_file(const char *filename, linker_file_t *result)
|
||||
return (error);
|
||||
}
|
||||
modules = !TAILQ_EMPTY(&lf->modules);
|
||||
linker_file_register_sysctls(lf);
|
||||
linker_file_register_sysctls(lf, false);
|
||||
linker_file_sysinit(lf);
|
||||
lf->flags |= LINKER_FILE_LINKED;
|
||||
|
||||
@ -443,6 +469,7 @@ linker_load_file(const char *filename, linker_file_t *result)
|
||||
linker_file_unload(lf, LINKER_UNLOAD_FORCE);
|
||||
return (ENOEXEC);
|
||||
}
|
||||
linker_file_enable_sysctls(lf);
|
||||
EVENTHANDLER_INVOKE(kld_load, lf);
|
||||
*result = lf;
|
||||
return (0);
|
||||
@ -692,8 +719,8 @@ linker_file_unload(linker_file_t file, int flags)
|
||||
*/
|
||||
if (file->flags & LINKER_FILE_LINKED) {
|
||||
file->flags &= ~LINKER_FILE_LINKED;
|
||||
linker_file_sysuninit(file);
|
||||
linker_file_unregister_sysctls(file);
|
||||
linker_file_sysuninit(file);
|
||||
}
|
||||
TAILQ_REMOVE(&linker_files, file, link);
|
||||
|
||||
@ -1642,7 +1669,7 @@ linker_preload(void *arg)
|
||||
if (linker_file_lookup_set(lf, "sysinit_set", &si_start,
|
||||
&si_stop, NULL) == 0)
|
||||
sysinit_add(si_start, si_stop);
|
||||
linker_file_register_sysctls(lf);
|
||||
linker_file_register_sysctls(lf, true);
|
||||
lf->flags |= LINKER_FILE_LINKED;
|
||||
continue;
|
||||
fail:
|
||||
|
@ -509,6 +509,37 @@ sysctl_register_oid(struct sysctl_oid *oidp)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sysctl_register_disabled_oid(struct sysctl_oid *oidp)
|
||||
{
|
||||
|
||||
/*
|
||||
* Mark the leaf as dormant if it's not to be immediately enabled.
|
||||
* We do not disable nodes as they can be shared between modules
|
||||
* and it is always safe to access a node.
|
||||
*/
|
||||
KASSERT((oidp->oid_kind & CTLFLAG_DORMANT) == 0,
|
||||
("internal flag is set in oid_kind"));
|
||||
if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
|
||||
oidp->oid_kind |= CTLFLAG_DORMANT;
|
||||
sysctl_register_oid(oidp);
|
||||
}
|
||||
|
||||
void
|
||||
sysctl_enable_oid(struct sysctl_oid *oidp)
|
||||
{
|
||||
|
||||
SYSCTL_ASSERT_WLOCKED();
|
||||
if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
|
||||
KASSERT((oidp->oid_kind & CTLFLAG_DORMANT) == 0,
|
||||
("sysctl node is marked as dormant"));
|
||||
return;
|
||||
}
|
||||
KASSERT((oidp->oid_kind & CTLFLAG_DORMANT) != 0,
|
||||
("enabling already enabled sysctl oid"));
|
||||
oidp->oid_kind &= ~CTLFLAG_DORMANT;
|
||||
}
|
||||
|
||||
void
|
||||
sysctl_unregister_oid(struct sysctl_oid *oidp)
|
||||
{
|
||||
@ -1057,7 +1088,7 @@ sysctl_sysctl_next_ls(struct sysctl_oid_list *lsp, int *name, u_int namelen,
|
||||
*next = oidp->oid_number;
|
||||
*oidpp = oidp;
|
||||
|
||||
if (oidp->oid_kind & CTLFLAG_SKIP)
|
||||
if ((oidp->oid_kind & (CTLFLAG_SKIP | CTLFLAG_DORMANT)) != 0)
|
||||
continue;
|
||||
|
||||
if (!namelen) {
|
||||
@ -1878,6 +1909,8 @@ sysctl_find_oid(int *name, u_int namelen, struct sysctl_oid **noid,
|
||||
}
|
||||
lsp = SYSCTL_CHILDREN(oid);
|
||||
} else if (indx == namelen) {
|
||||
if ((oid->oid_kind & CTLFLAG_DORMANT) != 0)
|
||||
return (ENOENT);
|
||||
*noid = oid;
|
||||
if (nindx != NULL)
|
||||
*nindx = indx;
|
||||
|
@ -83,6 +83,7 @@ struct ctlname {
|
||||
#define CTLFLAG_RD 0x80000000 /* Allow reads of variable */
|
||||
#define CTLFLAG_WR 0x40000000 /* Allow writes to the variable */
|
||||
#define CTLFLAG_RW (CTLFLAG_RD|CTLFLAG_WR)
|
||||
#define CTLFLAG_DORMANT 0x20000000 /* This sysctl is not active yet */
|
||||
#define CTLFLAG_ANYBODY 0x10000000 /* All users can set this var */
|
||||
#define CTLFLAG_SECURE 0x08000000 /* Permit set only if securelevel<=0 */
|
||||
#define CTLFLAG_PRISON 0x04000000 /* Prisoned roots can fiddle */
|
||||
@ -219,6 +220,8 @@ int sysctl_dpcpu_quad(SYSCTL_HANDLER_ARGS);
|
||||
* These functions are used to add/remove an oid from the mib.
|
||||
*/
|
||||
void sysctl_register_oid(struct sysctl_oid *oidp);
|
||||
void sysctl_register_disabled_oid(struct sysctl_oid *oidp);
|
||||
void sysctl_enable_oid(struct sysctl_oid *oidp);
|
||||
void sysctl_unregister_oid(struct sysctl_oid *oidp);
|
||||
|
||||
/* Declare a static oid to allow child oids to be added to it. */
|
||||
|
Loading…
Reference in New Issue
Block a user