Module Name: src Committed By: pgoyette Date: Tue Apr 3 08:29:44 UTC 2018
Modified Files: src/doc [pgoyette-compat]: COMPAT-branch-notes src/lib/libc/sys [pgoyette-compat]: modctl.2 src/sbin/modstat [pgoyette-compat]: main.c src/sys/compat/common [pgoyette-compat]: Makefile.sysio compat_80_mod.c compat_mod.h files.common src/sys/compat/netbsd32 [pgoyette-compat]: netbsd32_module.c src/sys/kern [pgoyette-compat]: compat_stub.c sys_module.c src/sys/modules/compat_80 [pgoyette-compat]: Makefile src/sys/sys [pgoyette-compat]: compat_stub.h module.h Added Files: src/sys/compat/common [pgoyette-compat]: kern_mod_80.c Log Message: Remove fixed allocation of modules' "required" lists (previously limited to MAXMODDEPS entries). Update the modctl(MODCTL_STAT) syscall to return the required data in a new format, and retain the previous data format in MODCTL_OSTAT. Update the compat_80 and compat_netbsd32 modules as needed. To generate a diff of this commit: cvs rdiff -u -r1.1.2.8 -r1.1.2.9 src/doc/COMPAT-branch-notes cvs rdiff -u -r1.13 -r1.13.4.1 src/lib/libc/sys/modctl.2 cvs rdiff -u -r1.24.12.1 -r1.24.12.2 src/sbin/modstat/main.c cvs rdiff -u -r1.7.18.19 -r1.7.18.20 src/sys/compat/common/Makefile.sysio cvs rdiff -u -r1.1.2.6 -r1.1.2.7 src/sys/compat/common/compat_80_mod.c cvs rdiff -u -r1.1.42.17 -r1.1.42.18 src/sys/compat/common/compat_mod.h cvs rdiff -u -r1.1.2.29 -r1.1.2.30 src/sys/compat/common/files.common cvs rdiff -u -r0 -r1.1.2.1 src/sys/compat/common/kern_mod_80.c cvs rdiff -u -r1.6 -r1.6.2.1 src/sys/compat/netbsd32/netbsd32_module.c cvs rdiff -u -r1.1.2.13 -r1.1.2.14 src/sys/kern/compat_stub.c cvs rdiff -u -r1.23.2.5 -r1.23.2.6 src/sys/kern/sys_module.c cvs rdiff -u -r1.1.2.2 -r1.1.2.3 src/sys/modules/compat_80/Makefile cvs rdiff -u -r1.1.2.13 -r1.1.2.14 src/sys/sys/compat_stub.h cvs rdiff -u -r1.41.14.7 -r1.41.14.8 src/sys/sys/module.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/doc/COMPAT-branch-notes diff -u src/doc/COMPAT-branch-notes:1.1.2.8 src/doc/COMPAT-branch-notes:1.1.2.9 --- src/doc/COMPAT-branch-notes:1.1.2.8 Wed Mar 28 03:41:26 2018 +++ src/doc/COMPAT-branch-notes Tue Apr 3 08:29:44 2018 @@ -27,6 +27,11 @@ DONE defopt/defflag lines in the config files* as needed, to insure that built-in dependencies get resolved. +8. Fixed limits on the number of module depedencies and maximum + recursion level have been removed. Previous code for reporting + module status to userland has been versioned and moved to the + compat_80 module. + TODO ---- Index: src/lib/libc/sys/modctl.2 diff -u src/lib/libc/sys/modctl.2:1.13 src/lib/libc/sys/modctl.2:1.13.4.1 --- src/lib/libc/sys/modctl.2:1.13 Mon Jul 3 21:32:50 2017 +++ src/lib/libc/sys/modctl.2 Tue Apr 3 08:29:44 2018 @@ -1,4 +1,4 @@ -.\" $NetBSD: modctl.2,v 1.13 2017/07/03 21:32:50 wiz Exp $ +.\" $NetBSD: modctl.2,v 1.13.4.1 2018/04/03 08:29:44 pgoyette Exp $ .\" .\" Copyright (c) 2009 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -74,9 +74,19 @@ In this case, the argument should be a .Em struct iovec pointing to a suitable block of memory. -The kernel will fill this block with an array of +The kernel will fill this block with +.Bl -bullet +.It +a count of the number of modules loaded (including aliases), +.It +an array of .Em modstat_t -structures, one per loaded module. +structures, one per loaded module, and +.It +a series of NUL-terminated strings containing the modules' +required modules lists. +.El +.Pp If the block is not large enough, the data returned will be truncated to fit. The kernel will then update the @@ -146,9 +156,6 @@ contains the following elements, which a .Bl -tag -width aaaaaaaa .It Fa "char ms_name[MAXMODNAME]" The name of the module. -.It Fa "char ms_required[MAXMODNAME * MAXMODDEPS]" -The list of modules required by this module -as a comma-delimited list of module names. .It Fa "modsrc_t ms_source" One of the following enumerated constants: .Bl -tag -compact -width "MODULE_SOURCE_FILESYS" @@ -172,14 +179,13 @@ Device driver. Executable file format. .It Dv MODULE_CLASS_MISC Miscellaneous. -.It Dv MODULE_CLASS_ANY -Any module class. -.\" XXX: is MODULE_CLASS_ANY ever returned by this interface? .El .It Fa "uint64_t ms_addr" The load address within the kernel. +(This value is available only for privileged users.) .It Fa "u_int ms_size" Loaded size of the module. +(This value is available only for privileged users.) .It Fa "u_int ms_refcnt" Current number of live references to this module. .It Fa "u_int ms_flags" @@ -190,6 +196,8 @@ The "force" flag must be specified to re .It Dv MODFLAG_AUTO_LOADED The module was auto-loaded by the operating system. .El +.It Fa "uint_ms_reqoffset" +The offset (in bytes) from the beginning of the required-module data. .El .Sh RETURN VALUES Upon successful completion, the value returned is 0. Index: src/sbin/modstat/main.c diff -u src/sbin/modstat/main.c:1.24.12.1 src/sbin/modstat/main.c:1.24.12.2 --- src/sbin/modstat/main.c:1.24.12.1 Sat Mar 10 10:33:40 2018 +++ src/sbin/modstat/main.c Tue Apr 3 08:29:44 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: main.c,v 1.24.12.1 2018/03/10 10:33:40 pgoyette Exp $ */ +/* $NetBSD: main.c,v 1.24.12.2 2018/04/03 08:29:44 pgoyette Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -28,7 +28,7 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID("$NetBSD: main.c,v 1.24.12.1 2018/03/10 10:33:40 pgoyette Exp $"); +__RCSID("$NetBSD: main.c,v 1.24.12.2 2018/04/03 08:29:44 pgoyette Exp $"); #endif /* !lint */ #include <sys/module.h> @@ -81,6 +81,7 @@ main(int argc, char **argv) int ch, rc, modauto = 1; size_t maxnamelen = 16, i, modautolen; char loadable = '\0'; + const char *reqoff, *req; bool address = false; name = NULL; @@ -177,29 +178,36 @@ main(int argc, char **argv) len = iov.iov_len; } - len = iov.iov_len / sizeof(modstat_t); - qsort(iov.iov_base, len, sizeof(modstat_t), modstatcmp); - for (i = 0, ms = iov.iov_base; i < len; i++, ms++) { + len = *(int *)iov.iov_base; + ms = (modstat_t *)((char *)iov.iov_base + sizeof(int)); + + qsort(ms, len, sizeof(modstat_t), modstatcmp); + for (i = 0; i < len; i++, ms++) { size_t namelen = strlen(ms->ms_name); if (maxnamelen < namelen) maxnamelen = namelen; } + ms = (modstat_t *)((char *)iov.iov_base + sizeof(int)); + reqoff = (char *)(&ms[len]); + printf("%-*s %-8s %-8s %-4s %5s ", (int)maxnamelen, "NAME", "CLASS", "SOURCE", "FLAG", "REFS"); if (address) printf("%-16s ", "ADDRESS"); printf("%7s %s \n", "SIZE", "REQUIRES"); - for (ms = iov.iov_base; len != 0; ms++, len--) { + + for (; len != 0; ms++, len--) { const char *class; const char *source; + if (ms->ms_reqoffset == 0) + req = "-"; + else { + req = &reqoff[ms->ms_reqoffset]; + } if (name != NULL && strcmp(ms->ms_name, name) != 0) { continue; } - if (ms->ms_required[0] == '\0') { - ms->ms_required[0] = '-'; - ms->ms_required[1] = '\0'; - } if (ms->ms_size == 0) { sbuf[0] = '-'; sbuf[1] = '\0'; @@ -221,7 +229,7 @@ main(int argc, char **argv) ms->ms_refcnt); if (address) printf("%-16" PRIx64 " ", ms->ms_addr); - printf("%7s %s\n", sbuf, ms->ms_required); + printf("%7s %s\n", sbuf, (req)); } exit(EXIT_SUCCESS); Index: src/sys/compat/common/Makefile.sysio diff -u src/sys/compat/common/Makefile.sysio:1.7.18.19 src/sys/compat/common/Makefile.sysio:1.7.18.20 --- src/sys/compat/common/Makefile.sysio:1.7.18.19 Thu Mar 29 23:23:03 2018 +++ src/sys/compat/common/Makefile.sysio Tue Apr 3 08:29:44 2018 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile.sysio,v 1.7.18.19 2018/03/29 23:23:03 pgoyette Exp $ +# $NetBSD: Makefile.sysio,v 1.7.18.20 2018/04/03 08:29:44 pgoyette Exp $ # Sources for syscall and ioctl compatibility across the versions. @@ -45,6 +45,9 @@ SRCS+= kern_sa_60.c tty_60.c kern_time_6 # Compatibility code for NetBSD 7.0 SRCS+= rtsock_70.c uipc_usrreq_70.c +# Compatability code for NetBSD 8.0 +SRCS+= kern_mod_80.c + # More compatibility code for NetBSD 5.0 .PATH: ${S}/opencrypto SRCS+= ocryptodev.c Index: src/sys/compat/common/compat_80_mod.c diff -u src/sys/compat/common/compat_80_mod.c:1.1.2.6 src/sys/compat/common/compat_80_mod.c:1.1.2.7 --- src/sys/compat/common/compat_80_mod.c:1.1.2.6 Sat Mar 24 08:10:49 2018 +++ src/sys/compat/common/compat_80_mod.c Tue Apr 3 08:29:44 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: compat_80_mod.c,v 1.1.2.6 2018/03/24 08:10:49 pgoyette Exp $ */ +/* $NetBSD: compat_80_mod.c,v 1.1.2.7 2018/04/03 08:29:44 pgoyette Exp $ */ /*- * Copyright (c) 2018 The NetBSD Foundation, Inc. @@ -34,7 +34,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: compat_80_mod.c,v 1.1.2.6 2018/03/24 08:10:49 pgoyette Exp $"); +__KERNEL_RCSID(0, "$NetBSD: compat_80_mod.c,v 1.1.2.7 2018/04/03 08:29:44 pgoyette Exp $"); #include <sys/systm.h> #include <sys/module.h> @@ -46,6 +46,7 @@ __KERNEL_RCSID(0, "$NetBSD: compat_80_mo int compat_80_init(void) { + kern_mod_80_init(); raidframe_80_init(); return 0; @@ -55,6 +56,7 @@ int compat_80_fini(void) { raidframe_80_fini(); + kern_mod_80_fini(); return 0; } Index: src/sys/compat/common/compat_mod.h diff -u src/sys/compat/common/compat_mod.h:1.1.42.17 src/sys/compat/common/compat_mod.h:1.1.42.18 --- src/sys/compat/common/compat_mod.h:1.1.42.17 Sat Mar 31 09:17:35 2018 +++ src/sys/compat/common/compat_mod.h Tue Apr 3 08:29:44 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: compat_mod.h,v 1.1.42.17 2018/03/31 09:17:35 pgoyette Exp $ */ +/* $NetBSD: compat_mod.h,v 1.1.42.18 2018/04/03 08:29:44 pgoyette Exp $ */ /*- * Copyright (c) 2013 The NetBSD Foundation, Inc. @@ -42,6 +42,8 @@ void compat_sysctl_vfs(struct sysctllog #ifdef COMPAT_80 int compat_80_init(void); int compat_80_fini(void); +void kern_mod_80_init(void); +void kern_mod_80_fini(void); #endif #ifdef COMPAT_70 Index: src/sys/compat/common/files.common diff -u src/sys/compat/common/files.common:1.1.2.29 src/sys/compat/common/files.common:1.1.2.30 --- src/sys/compat/common/files.common:1.1.2.29 Sat Mar 31 09:17:35 2018 +++ src/sys/compat/common/files.common Tue Apr 3 08:29:44 2018 @@ -1,4 +1,4 @@ -# $NetBSD: files.common,v 1.1.2.29 2018/03/31 09:17:35 pgoyette Exp $ +# $NetBSD: files.common,v 1.1.2.30 2018/04/03 08:29:44 pgoyette Exp $ # # Generic files, used by all compat options. @@ -96,6 +96,7 @@ file compat/common/uipc_usrreq_70.c com # Compatability code for NetBSD 8.0 file compat/common/compat_80_mod.c compat_80 +file compat/common/kern_mod_80.c compat_80 # # Sources for sysv ipc compatibility across the versions. Index: src/sys/compat/netbsd32/netbsd32_module.c diff -u src/sys/compat/netbsd32/netbsd32_module.c:1.6 src/sys/compat/netbsd32/netbsd32_module.c:1.6.2.1 --- src/sys/compat/netbsd32/netbsd32_module.c:1.6 Thu Jan 18 13:31:21 2018 +++ src/sys/compat/netbsd32/netbsd32_module.c Tue Apr 3 08:29:44 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: netbsd32_module.c,v 1.6 2018/01/18 13:31:21 maxv Exp $ */ +/* $NetBSD: netbsd32_module.c,v 1.6.2.1 2018/04/03 08:29:44 pgoyette Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: netbsd32_module.c,v 1.6 2018/01/18 13:31:21 maxv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: netbsd32_module.c,v 1.6.2.1 2018/04/03 08:29:44 pgoyette Exp $"); #include <sys/param.h> #include <sys/dirent.h> @@ -42,17 +42,37 @@ __KERNEL_RCSID(0, "$NetBSD: netbsd32_mod #include <compat/netbsd32/netbsd32_syscallargs.h> #include <compat/netbsd32/netbsd32_conv.h> +#ifdef COMPAT_80 +static void +copy_oalias(omodstat_t *oms, const char * const *aliasp, modinfo_t *mi, + module_t *mod) +{ + + strlcpy(oms->oms_name, *aliasp, sizeof(oms->oms_name)); + strlcpy(oms->oms_required, mi->mi_name, sizeof(oms->oms_required)); + oms->oms_class = mi->mi_class; + oms->oms_source = mod->mod_source; + oms->oms_flags = mod->mod_flags | MODFLG_IS_ALIAS; +} + static int -modctl32_handle_stat(struct netbsd32_iovec *iov, void *arg) +modctl32_handle_ostat(int cmd, struct netbsd32_iovec *iov, void *arg) { - modstat_t *ms, *mso; + omodstat_t *oms, *omso; modinfo_t *mi; module_t *mod; vaddr_t addr; size_t size; - size_t mslen; + size_t omslen; + size_t used; int error; + int omscnt; bool stataddr; + const char * const *aliasp; + const char *suffix = "..."; + + if (cmd != MODCTL_OSTAT) + return EINVAL; /* If not privileged, don't expose kernel addresses. */ error = kauth_authorize_system(kauth_cred_get(), KAUTH_SYSTEM_MODULE, @@ -60,34 +80,229 @@ modctl32_handle_stat(struct netbsd32_iov stataddr = (error == 0); kernconfig_lock(); - mslen = (module_count+module_builtinlist+1) * sizeof(modstat_t); - mso = kmem_zalloc(mslen, KM_SLEEP); - ms = mso; + omscnt = 0; TAILQ_FOREACH(mod, &module_list, mod_chain) { + omscnt++; mi = mod->mod_info; - strlcpy(ms->ms_name, mi->mi_name, sizeof(ms->ms_name)); + if ((aliasp = *mi->mi_aliases) != NULL) { + while (*aliasp++ != NULL) + omscnt++; + } + } + TAILQ_FOREACH(mod, &module_builtins, mod_chain) { + omscnt++; + mi = mod->mod_info; + if ((aliasp = *mi->mi_aliases) != NULL) { + while (*aliasp++ != NULL) + omscnt++; + } + } + omslen = omscnt * sizeof(omodstat_t); + omso = kmem_zalloc(omslen, KM_SLEEP); + oms = omso; + TAILQ_FOREACH(mod, &module_list, mod_chain) { + mi = mod->mod_info; + strlcpy(oms->oms_name, mi->mi_name, sizeof(oms->oms_name)); if (mi->mi_required != NULL) { - strlcpy(ms->ms_required, mi->mi_required, - sizeof(ms->ms_required)); + used = strlcpy(oms->oms_required, mi->mi_required, + sizeof(oms->oms_required)); + if (used >= sizeof(oms->oms_required)) { + oms->oms_required[sizeof(oms->oms_required) - + strlen(suffix) - 1] = '\0'; + strlcat(oms->oms_required, suffix, + sizeof(oms->oms_required)); + } } if (mod->mod_kobj != NULL && stataddr) { kobj_stat(mod->mod_kobj, &addr, &size); - ms->ms_addr = addr; - ms->ms_size = size; + oms->oms_addr = addr; + oms->oms_size = size; } - ms->ms_class = mi->mi_class; - ms->ms_refcnt = mod->mod_refcnt; - ms->ms_source = mod->mod_source; - ms->ms_flags = mod->mod_flags; + oms->oms_class = mi->mi_class; + oms->oms_refcnt = mod->mod_refcnt; + oms->oms_source = mod->mod_source; + oms->oms_flags = mod->mod_flags; + oms++; + aliasp = *mi->mi_aliases; + if (aliasp == NULL) + continue; + while (*aliasp) { + copy_oalias(oms, aliasp, mi, mod); + aliasp++; + oms++; + } + } + TAILQ_FOREACH(mod, &module_builtins, mod_chain) { + mi = mod->mod_info; + strlcpy(oms->oms_name, mi->mi_name, sizeof(oms->oms_name)); + if (mi->mi_required != NULL) { + used = strlcpy(oms->oms_required, mi->mi_required, + sizeof(oms->oms_required)); + if (used >= sizeof(oms->oms_required)) { + oms->oms_required[sizeof(oms->oms_required) - + strlen(suffix) - 1] = '\0'; + strlcat(oms->oms_required, suffix, + sizeof(oms->oms_required)); + } + } + if (mod->mod_kobj != NULL && stataddr) { + kobj_stat(mod->mod_kobj, &addr, &size); + oms->oms_addr = addr; + oms->oms_size = size; + } + oms->oms_class = mi->mi_class; + oms->oms_refcnt = -1; + KASSERT(mod->mod_source == MODULE_SOURCE_KERNEL); + oms->oms_source = mod->mod_source; + oms++; + aliasp = *mi->mi_aliases; + if (aliasp == NULL) + continue; + while (*aliasp) { + copy_oalias(oms, aliasp, mi, mod); + aliasp++; + oms++; + } + } + kernconfig_unlock(); + error = copyout(omso, NETBSD32PTR64(iov->iov_base), + min(omslen - sizeof(modstat_t), iov->iov_len)); + kmem_free(omso, omslen); + if (error == 0) { + iov->iov_len = omslen - sizeof(modstat_t); + error = copyout(iov, arg, sizeof(*iov)); + } + + return error; +} +#endif /* COMPAT_80 */ + +static void +copy_alias(modstat_t *ms, const char * const *aliasp, modinfo_t *mi, + module_t *mod) +{ + + strlcpy(ms->ms_name, *aliasp, sizeof(ms->ms_name)); + ms->ms_class = mi->mi_class; + ms->ms_source = mod->mod_source; + ms->ms_flags = mod->mod_flags | MODFLG_IS_ALIAS; + ms->ms_reqoffset = 0; +} + +static int +modctl32_handle_stat(struct netbsd32_iovec *iov, void *arg) +{ + int ms_cnt; + modstat_t *ms, *mso; + size_t ms_len; + int req_cnt; + char *req, *reqo; + size_t req_len; + char *out_p; + size_t out_s; + + modinfo_t *mi; + module_t *mod; + vaddr_t addr; + size_t size; + size_t used; + int off; + int error; + bool stataddr; + const char * const *aliasp; + + /* If not privileged, don't expose kernel addresses. */ + error = kauth_authorize_system(kauth_cred_get(), KAUTH_SYSTEM_MODULE, + 0, (void *)(uintptr_t)MODCTL_STAT, NULL, NULL); + stataddr = (error == 0); + + kernconfig_lock(); + ms_cnt = 0; + req_len = 1; + + /* + * Count up the number of modstat_t needed, and total size of + * require_module lists on both active and built-in lists + */ + TAILQ_FOREACH(mod, &module_list, mod_chain) { + ms_cnt++; + mi = mod->mod_info; + if ((aliasp = *mi->mi_aliases) != NULL) { + while (*aliasp++ != NULL) + ms_cnt++; + } + if (mi->mi_required != NULL) { + req_cnt++; + req_len += strlen(mi->mi_required) + 1; + } + } + TAILQ_FOREACH(mod, &module_builtins, mod_chain) { + ms_cnt++; + mi = mod->mod_info; + if ((aliasp = *mi->mi_aliases) != NULL) { + while (*aliasp++ != NULL) + ms_cnt++; + } + if (mi->mi_required != NULL) { + req_cnt++; + req_len += strlen(mi->mi_required) + 1; + } + } + + /* Allocate internal buffers to hold all the output data */ + ms_len = ms_cnt * sizeof(modstat_t); + ms = kmem_zalloc(ms_len, KM_SLEEP); + req = kmem_zalloc(req_len, KM_SLEEP); + + mso = ms; + reqo = req++; + off = 1; + + /* + * Load data into our internal buffers for both active and + * build-in module lists + */ + TAILQ_FOREACH(mod, &module_list, mod_chain) { + mi = mod->mod_info; + strlcpy(ms->ms_name, mi->mi_name, sizeof(ms->ms_name)); + if (mi->mi_required != NULL) { + ms->ms_reqoffset = off; + used = strlcpy(req, mi->mi_required, req_len - off); + KASSERTMSG(used < req_len - off, "reqlist grew!"); + off = used + 1; + req += used + 1; + } else + ms->ms_reqoffset = 0; + if (mod->mod_kobj != NULL && stataddr) { + kobj_stat(mod->mod_kobj, &addr, &size); + ms->ms_addr = addr; + ms->ms_size = size; + } + ms->ms_class = mi->mi_class; + ms->ms_refcnt = mod->mod_refcnt; + ms->ms_source = mod->mod_source; + ms->ms_flags = mod->mod_flags; + ms++; + aliasp = *mi->mi_aliases; + if (aliasp == NULL) + continue; + while (*aliasp) { + copy_alias(ms, aliasp, mi, mod); + aliasp++; ms++; + } } TAILQ_FOREACH(mod, &module_builtins, mod_chain) { mi = mod->mod_info; strlcpy(ms->ms_name, mi->mi_name, sizeof(ms->ms_name)); if (mi->mi_required != NULL) { - strlcpy(ms->ms_required, mi->mi_required, - sizeof(ms->ms_required)); - } + ms->ms_reqoffset = off; + used = strlcpy(req, mi->mi_required, req_len - off); + KASSERTMSG(used < req_len - off, "reqlist grew!"); + off += used + 1; + req += used + 1; + } else + ms->ms_reqoffset = 0; if (mod->mod_kobj != NULL && stataddr) { kobj_stat(mod->mod_kobj, &addr, &size); ms->ms_addr = addr; @@ -98,19 +313,58 @@ modctl32_handle_stat(struct netbsd32_iov KASSERT(mod->mod_source == MODULE_SOURCE_KERNEL); ms->ms_source = mod->mod_source; ms++; + aliasp = *mi->mi_aliases; + if (aliasp == NULL) + continue; + while (*aliasp) { + copy_alias(ms, aliasp, mi, mod); + aliasp++; + ms++; + } } kernconfig_unlock(); - error = copyout(mso, NETBSD32PTR64(iov->iov_base), - min(mslen - sizeof(modstat_t), iov->iov_len)); - kmem_free(mso, mslen); + + /* + * Now copyout our internal buffers back to userland + */ + out_p = NETBSD32PTR64(iov->iov_base); + out_s = iov->iov_len; + size = sizeof(ms_cnt); + + /* Copy out the count of modstat_t */ + if (out_s) { + size = min(sizeof(ms_cnt), out_s); + error = copyout(&ms_cnt, out_p, size); + out_p += size; + out_s -= size; + } + /* Copy out the modstat_t array */ + if (out_s && error == 0) { + size = min(ms_len, out_s); + error = copyout(mso, out_p, size); + out_p += size; + out_s -= size; + } + /* Copy out the "required" strings */ + if (out_s && error == 0) { + size = min(req_len, out_s); + error = copyout(reqo, out_p, size); + out_p += size; + out_s -= size; + } + kmem_free(mso, ms_len); + kmem_free(reqo, req_len); + + /* Finally, update the userland copy of the iovec's length */ if (error == 0) { - iov->iov_len = mslen - sizeof(modstat_t); + iov->iov_len = ms_len + req_len + sizeof(ms_cnt); error = copyout(iov, arg, sizeof(*iov)); } return error; } + int netbsd32_modctl(struct lwp *lwp, const struct netbsd32_modctl_args *uap, register_t *result) @@ -146,6 +400,16 @@ netbsd32_modctl(struct lwp *lwp, const s } break; +#ifdef COMPAT_80 + case MODCTL_OSTAT: + error = copyin(arg, &iov, sizeof(iov)); + if (error != 0) { + break; + } + error = modctl32_handle_ostat(SCARG(uap, cmd), &iov, arg); + break; +#endif + case MODCTL_STAT: error = copyin(arg, &iov, sizeof(iov)); if (error != 0) { Index: src/sys/kern/compat_stub.c diff -u src/sys/kern/compat_stub.c:1.1.2.13 src/sys/kern/compat_stub.c:1.1.2.14 --- src/sys/kern/compat_stub.c:1.1.2.13 Fri Mar 30 10:09:08 2018 +++ src/sys/kern/compat_stub.c Tue Apr 3 08:29:44 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: compat_stub.c,v 1.1.2.13 2018/03/30 10:09:08 pgoyette Exp $ */ +/* $NetBSD: compat_stub.c,v 1.1.2.14 2018/04/03 08:29:44 pgoyette Exp $ */ /*- * Copyright (c) 2018 The NetBSD Foundation, Inc. @@ -148,3 +148,8 @@ int (*if43_20_cvtcmd)(int) = (void *)eno void (*rtsock14_oifmsg)(struct ifnet *) = (void *)voidop; int (*rtsock14_iflist)(struct ifnet *, struct rt_walkarg *, struct rt_addrinfo *, size_t) = (void *)enosys; + +/* + * modctl handler for old style OSTAT + */ +int (*compat_modstat_80)(int, struct iovec *, void *) = (void *)enosys; Index: src/sys/kern/sys_module.c diff -u src/sys/kern/sys_module.c:1.23.2.5 src/sys/kern/sys_module.c:1.23.2.6 --- src/sys/kern/sys_module.c:1.23.2.5 Sun Apr 1 23:06:11 2018 +++ src/sys/kern/sys_module.c Tue Apr 3 08:29:44 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: sys_module.c,v 1.23.2.5 2018/04/01 23:06:11 pgoyette Exp $ */ +/* $NetBSD: sys_module.c,v 1.23.2.6 2018/04/03 08:29:44 pgoyette Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -31,7 +31,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sys_module.c,v 1.23.2.5 2018/04/01 23:06:11 pgoyette Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sys_module.c,v 1.23.2.6 2018/04/03 08:29:44 pgoyette Exp $"); #ifdef _KERNEL_OPT #include "opt_modular.h" @@ -47,6 +47,7 @@ __KERNEL_RCSID(0, "$NetBSD: sys_module.c #include <sys/module.h> #include <sys/syscall.h> #include <sys/syscallargs.h> +#include <sys/compat_stub.h> /* * Arbitrary limit to avoid DoS for excessive memory allocation. @@ -116,27 +117,33 @@ copy_alias(modstat_t *ms, const char * c { strlcpy(ms->ms_name, *aliasp, sizeof(ms->ms_name)); - strlcpy(ms->ms_required, mi->mi_name, sizeof(ms->ms_required)); ms->ms_class = mi->mi_class; ms->ms_source = mod->mod_source; ms->ms_flags = mod->mod_flags | MODFLG_IS_ALIAS; + ms->ms_reqoffset = 0; } static int handle_modctl_stat(struct iovec *iov, void *arg) { + int ms_cnt; modstat_t *ms, *mso; + size_t ms_len; + int req_cnt; + char *req, *reqo; + size_t req_len; + char *out_p; + size_t out_s; + modinfo_t *mi; module_t *mod; vaddr_t addr; size_t size; - size_t mslen; size_t used; + int off; int error; - int mscnt; bool stataddr; const char * const *aliasp; - const char *suffix = "..."; /* If not privileged, don't expose kernel addresses. */ error = kauth_authorize_system(kauth_cred_get(), KAUTH_SYSTEM_MODULE, @@ -144,31 +151,62 @@ handle_modctl_stat(struct iovec *iov, vo stataddr = (error == 0); kernconfig_lock(); - mscnt = 0; + ms_cnt = 0; + req_len = 1; + + /* + * Count up the number of modstat_t needed, and total size of + * require_module lists on both active and built-in lists + */ TAILQ_FOREACH(mod, &module_list, mod_chain) { - mscnt++; + ms_cnt++; mi = mod->mod_info; if ((aliasp = *mi->mi_aliases) != NULL) { while (*aliasp++ != NULL) - mslen++; + ms_cnt++; + } + if (mi->mi_required != NULL) { + req_cnt++; + req_len += strlen(mi->mi_required) + 1; + } + } + TAILQ_FOREACH(mod, &module_builtins, mod_chain) { + ms_cnt++; + mi = mod->mod_info; + if ((aliasp = *mi->mi_aliases) != NULL) { + while (*aliasp++ != NULL) + ms_cnt++; + } + if (mi->mi_required != NULL) { + req_cnt++; + req_len += strlen(mi->mi_required) + 1; } } - mslen = (mscnt+module_builtinlist+1) * sizeof(modstat_t); - mso = kmem_zalloc(mslen, KM_SLEEP); - ms = mso; + + /* Allocate internal buffers to hold all the output data */ + ms_len = ms_cnt * sizeof(modstat_t); + ms = kmem_zalloc(ms_len, KM_SLEEP); + req = kmem_zalloc(req_len, KM_SLEEP); + + mso = ms; + reqo = req++; + off = 1; + + /* + * Load data into our internal buffers for both active and + * build-in module lists + */ TAILQ_FOREACH(mod, &module_list, mod_chain) { mi = mod->mod_info; strlcpy(ms->ms_name, mi->mi_name, sizeof(ms->ms_name)); if (mi->mi_required != NULL) { - used = strlcpy(ms->ms_required, mi->mi_required, - sizeof(ms->ms_required)); - if (used >= sizeof(ms->ms_required)) { - ms->ms_required[sizeof(ms->ms_required) - - strlen(suffix) - 1] = '\0'; - strlcat(ms->ms_required, suffix, - sizeof(ms->ms_required)); - } - } + ms->ms_reqoffset = off; + used = strlcpy(req, mi->mi_required, req_len - off); + KASSERTMSG(used < req_len - off, "reqlist grew!"); + off += used + 1; + req += used + 1; + } else + ms->ms_reqoffset = 0; if (mod->mod_kobj != NULL && stataddr) { kobj_stat(mod->mod_kobj, &addr, &size); ms->ms_addr = addr; @@ -192,13 +230,13 @@ handle_modctl_stat(struct iovec *iov, vo mi = mod->mod_info; strlcpy(ms->ms_name, mi->mi_name, sizeof(ms->ms_name)); if (mi->mi_required != NULL) { - *ms->ms_required = '\0'; - used = strlcat(ms->ms_required, mi->mi_required, - sizeof(ms->ms_required) - 6); - if (used > sizeof(ms->ms_required) - 6) - strlcat(ms->ms_required, ", ...", - sizeof(ms->ms_required)); - } + ms->ms_reqoffset = off; + used = strlcpy(req, mi->mi_required, req_len - off); + KASSERTMSG(used < req_len - off, "reqlist grew!"); + off += used + 1; + req += used + 1; + } else + ms->ms_reqoffset = 0; if (mod->mod_kobj != NULL && stataddr) { kobj_stat(mod->mod_kobj, &addr, &size); ms->ms_addr = addr; @@ -219,11 +257,41 @@ handle_modctl_stat(struct iovec *iov, vo } } kernconfig_unlock(); - error = copyout(mso, iov->iov_base, - min(mslen - sizeof(modstat_t), iov->iov_len)); - kmem_free(mso, mslen); + + /* + * Now copyout our internal buffers back to userland + */ + out_p = iov->iov_base; + out_s = iov->iov_len; + size = sizeof(ms_cnt); + + /* Copy out the count of modstat_t */ + if (out_s) { + size = min(sizeof(ms_cnt), out_s); + error = copyout(&ms_cnt, out_p, size); + out_p += size; + out_s -= size; + } + /* Copy out the modstat_t array */ + if (out_s && error == 0) { + size = min(ms_len, out_s); + error = copyout(mso, out_p, size); + out_p += size; + out_s -= size; + } + /* Copy out the "required" strings */ + if (out_s && error == 0) { + size = min(req_len, out_s); + error = copyout(reqo, out_p, size); + out_p += size; + out_s -= size; + } + kmem_free(mso, ms_len); + kmem_free(reqo, req_len); + + /* Finally, update the userland copy of the iovec's length */ if (error == 0) { - iov->iov_len = mslen - sizeof(modstat_t); + iov->iov_len = ms_len + req_len + sizeof(ms_cnt); error = copyout(iov, arg, sizeof(*iov)); } @@ -286,7 +354,6 @@ sys_modctl(struct lwp *l, const struct s (void *)(uintptr_t)MODCTL_LOAD, (void *)loadtype, NULL); break; - default: error = EINVAL; break; @@ -295,7 +362,9 @@ sys_modctl(struct lwp *l, const struct s break; default: - error = EINVAL; + error = (*compat_modstat_80)(SCARG(uap, cmd), &iov, arg); + if (error == ENOSYS) + error = EINVAL; break; } Index: src/sys/modules/compat_80/Makefile diff -u src/sys/modules/compat_80/Makefile:1.1.2.2 src/sys/modules/compat_80/Makefile:1.1.2.3 --- src/sys/modules/compat_80/Makefile:1.1.2.2 Sat Mar 24 01:59:16 2018 +++ src/sys/modules/compat_80/Makefile Tue Apr 3 08:29:44 2018 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.1.2.2 2018/03/24 01:59:16 pgoyette Exp $ +# $NetBSD: Makefile,v 1.1.2.3 2018/04/03 08:29:44 pgoyette Exp $ .include "../Makefile.inc" @@ -9,6 +9,7 @@ KMOD= compat_80 CPPFLAGS+= -DCOMPAT_80 SRCS+= compat_80_mod.c +SRCS+= kern_mod_80.c .PATH: ${S}/dev/raidframe Index: src/sys/sys/compat_stub.h diff -u src/sys/sys/compat_stub.h:1.1.2.13 src/sys/sys/compat_stub.h:1.1.2.14 --- src/sys/sys/compat_stub.h:1.1.2.13 Fri Mar 30 10:09:08 2018 +++ src/sys/sys/compat_stub.h Tue Apr 3 08:29:44 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: compat_stub.h,v 1.1.2.13 2018/03/30 10:09:08 pgoyette Exp $ */ +/* $NetBSD: compat_stub.h,v 1.1.2.14 2018/04/03 08:29:44 pgoyette Exp $ */ /*- * Copyright (c) 2018 The NetBSD Foundation, Inc. @@ -171,4 +171,10 @@ extern void (*rtsock14_oifmsg)(struct if extern int (*rtsock14_iflist)(struct ifnet *, struct rt_walkarg *, struct rt_addrinfo *, size_t); +/* + * modctl handler for old style OSTAT + */ +struct iovec; + +extern int (*compat_modstat_80)(int, struct iovec *, void *); #endif /* _SYS_COMPAT_STUB_H */ Index: src/sys/sys/module.h diff -u src/sys/sys/module.h:1.41.14.7 src/sys/sys/module.h:1.41.14.8 --- src/sys/sys/module.h:1.41.14.7 Sun Apr 1 23:06:11 2018 +++ src/sys/sys/module.h Tue Apr 3 08:29:44 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: module.h,v 1.41.14.7 2018/04/01 23:06:11 pgoyette Exp $ */ +/* $NetBSD: module.h,v 1.41.14.8 2018/04/03 08:29:44 pgoyette Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -240,24 +240,41 @@ typedef struct modctl_load { typedef enum modctl { MODCTL_LOAD, /* modctl_load_t *ml */ MODCTL_UNLOAD, /* char *name */ - MODCTL_STAT, /* struct iovec *buffer */ - MODCTL_EXISTS /* enum: 0: load, 1: autoload */ + MODCTL_OSTAT, /* struct iovec *buffer */ + MODCTL_EXISTS, /* enum: 0: load, 1: autoload */ + MODCTL_STAT /* struct iovec *buffer */ } modctl_t; /* * This structure intentionally has the same layout for 32 and 64 * bit builds. */ +typedef struct omodstat { + char oms_name[MAXMODNAME]; + char oms_required[MAXMODNAME * MAXMODDEPS]; + uint64_t oms_addr; + modsrc_t oms_source; + modclass_t oms_class; + u_int oms_size; + u_int oms_refcnt; + u_int oms_flags; + u_int oms_reserved[3]; +} omodstat_t; + +/* + * This structure is used with the newer version of MODCTL_STAT, which + * exports strings of arbitrary length for the list of required modules. + */ typedef struct modstat { char ms_name[MAXMODNAME]; - char ms_required[MAXMODNAME * MAXMODDEPS]; uint64_t ms_addr; modsrc_t ms_source; modclass_t ms_class; u_int ms_size; u_int ms_refcnt; u_int ms_flags; - u_int ms_reserved[3]; + u_int ms_reqoffset; /* offset to module's required list + from beginning of iovec buffer! */ } modstat_t; int modctl(int, void *); Added files: Index: src/sys/compat/common/kern_mod_80.c diff -u /dev/null src/sys/compat/common/kern_mod_80.c:1.1.2.1 --- /dev/null Tue Apr 3 08:29:44 2018 +++ src/sys/compat/common/kern_mod_80.c Tue Apr 3 08:29:44 2018 @@ -0,0 +1,203 @@ +/* $NetBSD: kern_mod_80.c,v 1.1.2.1 2018/04/03 08:29:44 pgoyette Exp $ */ + +/*- + * Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * System calls relating to loadable modules. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: kern_mod_80.c,v 1.1.2.1 2018/04/03 08:29:44 pgoyette Exp $"); + +#ifdef _KERNEL_OPT +#include "opt_modular.h" +#endif + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/namei.h> +#include <sys/kauth.h> +#include <sys/kmem.h> +#include <sys/kobj.h> +#include <sys/module.h> +#include <sys/syscall.h> +#include <sys/syscallargs.h> +#include <sys/compat_stub.h> + +#include <compat/common/compat_mod.h> + +static void +copy_oalias(omodstat_t *oms, const char * const *aliasp, modinfo_t *mi, + module_t *mod) +{ + + strlcpy(oms->oms_name, *aliasp, sizeof(oms->oms_name)); + strlcpy(oms->oms_required, mi->mi_name, sizeof(oms->oms_required)); + oms->oms_class = mi->mi_class; + oms->oms_source = mod->mod_source; + oms->oms_flags = mod->mod_flags | MODFLG_IS_ALIAS; +} + +static int +compat_80_modstat(int cmd, struct iovec *iov, void *arg) +{ + omodstat_t *oms, *omso; + modinfo_t *mi; + module_t *mod; + vaddr_t addr; + size_t size; + size_t omslen; + size_t used; + int error; + int omscnt; + bool stataddr; + const char * const *aliasp; + const char *suffix = "..."; + + if (cmd != MODCTL_OSTAT) + return EINVAL; + + error = copyin(arg, iov, sizeof(*iov)); + if (error != 0) { + return error; + } + + /* If not privileged, don't expose kernel addresses. */ + error = kauth_authorize_system(kauth_cred_get(), KAUTH_SYSTEM_MODULE, + 0, (void *)(uintptr_t)MODCTL_STAT, NULL, NULL); + stataddr = (error == 0); + + kernconfig_lock(); + omscnt = 0; + TAILQ_FOREACH(mod, &module_list, mod_chain) { + omscnt++; + mi = mod->mod_info; + if ((aliasp = *mi->mi_aliases) != NULL) { + while (*aliasp++ != NULL) + omscnt++; + } + } + TAILQ_FOREACH(mod, &module_builtins, mod_chain) { + omscnt++; + mi = mod->mod_info; + if ((aliasp = *mi->mi_aliases) != NULL) { + while (*aliasp++ != NULL) + omscnt++; + } + } + omslen = omscnt * sizeof(omodstat_t); + omso = kmem_zalloc(omslen, KM_SLEEP); + oms = omso; + TAILQ_FOREACH(mod, &module_list, mod_chain) { + mi = mod->mod_info; + strlcpy(oms->oms_name, mi->mi_name, sizeof(oms->oms_name)); + if (mi->mi_required != NULL) { + used = strlcpy(oms->oms_required, mi->mi_required, + sizeof(oms->oms_required)); + if (used >= sizeof(oms->oms_required)) { + oms->oms_required[sizeof(oms->oms_required) - + strlen(suffix) - 1] = '\0'; + strlcat(oms->oms_required, suffix, + sizeof(oms->oms_required)); + } + } + if (mod->mod_kobj != NULL && stataddr) { + kobj_stat(mod->mod_kobj, &addr, &size); + oms->oms_addr = addr; + oms->oms_size = size; + } + oms->oms_class = mi->mi_class; + oms->oms_refcnt = mod->mod_refcnt; + oms->oms_source = mod->mod_source; + oms->oms_flags = mod->mod_flags; + oms++; + aliasp = *mi->mi_aliases; + if (aliasp == NULL) + continue; + while (*aliasp) { + copy_oalias(oms, aliasp, mi, mod); + aliasp++; + oms++; + } + } + TAILQ_FOREACH(mod, &module_builtins, mod_chain) { + mi = mod->mod_info; + strlcpy(oms->oms_name, mi->mi_name, sizeof(oms->oms_name)); + if (mi->mi_required != NULL) { + used = strlcpy(oms->oms_required, mi->mi_required, + sizeof(oms->oms_required)); + if (used >= sizeof(oms->oms_required)) { + oms->oms_required[sizeof(oms->oms_required) - + strlen(suffix) - 1] = '\0'; + strlcat(oms->oms_required, suffix, + sizeof(oms->oms_required)); + } + } + if (mod->mod_kobj != NULL && stataddr) { + kobj_stat(mod->mod_kobj, &addr, &size); + oms->oms_addr = addr; + oms->oms_size = size; + } + oms->oms_class = mi->mi_class; + oms->oms_refcnt = -1; + KASSERT(mod->mod_source == MODULE_SOURCE_KERNEL); + oms->oms_source = mod->mod_source; + oms++; + aliasp = *mi->mi_aliases; + if (aliasp == NULL) + continue; + while (*aliasp) { + copy_oalias(oms, aliasp, mi, mod); + aliasp++; + oms++; + } + } + kernconfig_unlock(); + error = copyout(omso, iov->iov_base, min(omslen, iov->iov_len)); + kmem_free(omso, omslen); + if (error == 0) { + iov->iov_len = omslen; + error = copyout(iov, arg, sizeof(*iov)); + } + + return error; +} + +void +kern_mod_80_init(void) +{ + + compat_modstat_80 = compat_80_modstat; +} + +void +kern_mod_80_fini(void) +{ + + compat_modstat_80 = (void *)enosys; +}