Author: avg
Date: Thu Oct  5 12:32:14 2017
New Revision: 324311
URL: https://svnweb.freebsd.org/changeset/base/324311

Log:
  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

Modified:
  head/sys/kern/kern_linker.c
  head/sys/kern/kern_sysctl.c
  head/sys/sys/sysctl.h

Modified: head/sys/kern/kern_linker.c
==============================================================================
--- head/sys/kern/kern_linker.c Thu Oct  5 12:29:34 2017        (r324310)
+++ head/sys/kern/kern_linker.c Thu Oct  5 12:32:14 2017        (r324311)
@@ -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;
 
@@ -303,8 +303,34 @@ linker_file_register_sysctls(linker_file_t lf)
 
        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 *
                                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 *
                                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 @@ restart:
                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:

Modified: head/sys/kern/kern_sysctl.c
==============================================================================
--- head/sys/kern/kern_sysctl.c Thu Oct  5 12:29:34 2017        (r324310)
+++ head/sys/kern/kern_sysctl.c Thu Oct  5 12:32:14 2017        (r324311)
@@ -510,6 +510,37 @@ retry:
 }
 
 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)
 {
        struct sysctl_oid *p;
@@ -1057,7 +1088,7 @@ sysctl_sysctl_next_ls(struct sysctl_oid_list *lsp, int
                *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 sysct
                        }
                        lsp = SYSCTL_CHILDREN(oid);
                } else if (indx == namelen) {
+                       if ((oid->oid_kind & CTLFLAG_DORMANT) != 0)
+                               return (ENOENT);
                        *noid = oid;
                        if (nindx != NULL)
                                *nindx = indx;

Modified: head/sys/sys/sysctl.h
==============================================================================
--- head/sys/sys/sysctl.h       Thu Oct  5 12:29:34 2017        (r324310)
+++ head/sys/sys/sysctl.h       Thu Oct  5 12:32:14 2017        (r324311)
@@ -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. */
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to