Index: stable/12/sys/kern/imgact_binmisc.c =================================================================== --- stable/12/sys/kern/imgact_binmisc.c (revision 367656) +++ stable/12/sys/kern/imgact_binmisc.c (revision 367657) @@ -1,762 +1,756 @@ /*- * Copyright (c) 2013-16, Stacey D. Son * 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 AUTHOR 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #include -#include -#include -#include #include #include #include #include -#include #include #include #include +#include #include #include #include /** * Miscellaneous binary interpreter image activator. * * If the given target executable's header matches 'xbe_magic' field in the * 'interpreter_list' then it will use the user-level interpreter specified in * the 'xbe_interpreter' field to execute the binary. The 'xbe_magic' field may * be adjusted to a given offset using the value in the 'xbe_moffset' field * and bits of the header may be masked using the 'xbe_mask' field. The * 'interpreter_list' entries are managed using sysctl(3) as described in the * file. */ /* * Node of the interpreter list. */ typedef struct imgact_binmisc_entry { + SLIST_ENTRY(imgact_binmisc_entry) link; char *ibe_name; uint8_t *ibe_magic; - uint32_t ibe_moffset; - uint32_t ibe_msize; uint8_t *ibe_mask; uint8_t *ibe_interpreter; + ssize_t ibe_interp_offset; uint32_t ibe_interp_argcnt; uint32_t ibe_interp_length; + uint32_t ibe_argv0_cnt; uint32_t ibe_flags; - SLIST_ENTRY(imgact_binmisc_entry) link; + uint32_t ibe_moffset; + uint32_t ibe_msize; } imgact_binmisc_entry_t; /* * sysctl() commands. */ #define IBC_ADD 1 /* Add given entry. */ #define IBC_REMOVE 2 /* Remove entry for a given name. */ #define IBC_DISABLE 3 /* Disable entry for a given name. */ #define IBC_ENABLE 4 /* Enable entry for a given name. */ #define IBC_LOOKUP 5 /* Lookup and return entry for given name. */ #define IBC_LIST 6 /* Get a snapshot of the interpretor list. */ /* * Interpreter string macros. * * They all start with '#' followed by a single letter: */ #define ISM_POUND '#' /* "##" is the escape sequence for single #. */ #define ISM_OLD_ARGV0 'a' /* "#a" is replaced with the old argv0. */ MALLOC_DEFINE(M_BINMISC, KMOD_NAME, "misc binary image activator"); /* The interpreter list. */ static SLIST_HEAD(, imgact_binmisc_entry) interpreter_list = SLIST_HEAD_INITIALIZER(interpreter_list); -static int interp_list_entry_count = 0; +static int interp_list_entry_count; static struct sx interp_list_sx; +#define INTERP_LIST_WLOCK() sx_xlock(&interp_list_sx) +#define INTERP_LIST_RLOCK() sx_slock(&interp_list_sx) +#define INTERP_LIST_WUNLOCK() sx_xunlock(&interp_list_sx) +#define INTERP_LIST_RUNLOCK() sx_sunlock(&interp_list_sx) + +#define INTERP_LIST_LOCK_INIT() sx_init(&interp_list_sx, KMOD_NAME) +#define INTERP_LIST_LOCK_DESTROY() sx_destroy(&interp_list_sx) + +#define INTERP_LIST_ASSERT_LOCKED() sx_assert(&interp_list_sx, SA_LOCKED) + /* * Populate the entry with the information about the interpreter. */ static void imgact_binmisc_populate_interp(char *str, imgact_binmisc_entry_t *ibe) { uint32_t len = 0, argc = 1; char t[IBE_INTERP_LEN_MAX]; char *sp, *tp; memset(t, 0, sizeof(t)); /* * Normalize interpreter string. Replace white space between args with * single space. */ sp = str; tp = t; while (*sp != '\0') { if (*sp == ' ' || *sp == '\t') { if (++len >= IBE_INTERP_LEN_MAX) break; *tp++ = ' '; argc++; while (*sp == ' ' || *sp == '\t') sp++; continue; } else { *tp++ = *sp++; len++; } } *tp = '\0'; len++; ibe->ibe_interpreter = malloc(len, M_BINMISC, M_WAITOK|M_ZERO); /* Populate all the ibe fields for the interpreter. */ memcpy(ibe->ibe_interpreter, t, len); ibe->ibe_interp_argcnt = argc; ibe->ibe_interp_length = len; } /* * Allocate memory and populate a new entry for the interpreter table. */ static imgact_binmisc_entry_t * -imgact_binmisc_new_entry(ximgact_binmisc_entry_t *xbe) +imgact_binmisc_new_entry(ximgact_binmisc_entry_t *xbe, ssize_t interp_offset, + int argv0_cnt) { imgact_binmisc_entry_t *ibe = NULL; size_t namesz = min(strlen(xbe->xbe_name) + 1, IBE_NAME_MAX); ibe = malloc(sizeof(*ibe), M_BINMISC, M_WAITOK|M_ZERO); ibe->ibe_name = malloc(namesz, M_BINMISC, M_WAITOK|M_ZERO); strlcpy(ibe->ibe_name, xbe->xbe_name, namesz); imgact_binmisc_populate_interp(xbe->xbe_interpreter, ibe); ibe->ibe_magic = malloc(xbe->xbe_msize, M_BINMISC, M_WAITOK|M_ZERO); memcpy(ibe->ibe_magic, xbe->xbe_magic, xbe->xbe_msize); ibe->ibe_mask = malloc(xbe->xbe_msize, M_BINMISC, M_WAITOK|M_ZERO); memcpy(ibe->ibe_mask, xbe->xbe_mask, xbe->xbe_msize); ibe->ibe_moffset = xbe->xbe_moffset; ibe->ibe_msize = xbe->xbe_msize; ibe->ibe_flags = xbe->xbe_flags; - + ibe->ibe_interp_offset = interp_offset; + ibe->ibe_argv0_cnt = argv0_cnt; return (ibe); } /* * Free the allocated memory for a given list item. */ static void imgact_binmisc_destroy_entry(imgact_binmisc_entry_t *ibe) { if (!ibe) return; if (ibe->ibe_magic) free(ibe->ibe_magic, M_BINMISC); if (ibe->ibe_mask) free(ibe->ibe_mask, M_BINMISC); if (ibe->ibe_interpreter) free(ibe->ibe_interpreter, M_BINMISC); if (ibe->ibe_name) free(ibe->ibe_name, M_BINMISC); if (ibe) free(ibe, M_BINMISC); } /* * Find the interpreter in the list by the given name. Return NULL if not * found. */ static imgact_binmisc_entry_t * imgact_binmisc_find_entry(char *name) { imgact_binmisc_entry_t *ibe; - sx_assert(&interp_list_sx, SA_LOCKED); + INTERP_LIST_ASSERT_LOCKED(); SLIST_FOREACH(ibe, &interpreter_list, link) { if (strncmp(name, ibe->ibe_name, IBE_NAME_MAX) == 0) return (ibe); } return (NULL); } /* * Add the given interpreter if it doesn't already exist. Return EEXIST * if the name already exist in the interpreter list. */ static int imgact_binmisc_add_entry(ximgact_binmisc_entry_t *xbe) { imgact_binmisc_entry_t *ibe; char *p; - int cnt; + ssize_t interp_offset; + int argv0_cnt, cnt; if (xbe->xbe_msize > IBE_MAGIC_MAX) return (EINVAL); + if (xbe->xbe_moffset + xbe->xbe_msize > IBE_MATCH_MAX) + return (EINVAL); for(cnt = 0, p = xbe->xbe_name; *p != 0; cnt++, p++) if (cnt >= IBE_NAME_MAX || !isascii((int)*p)) return (EINVAL); for(cnt = 0, p = xbe->xbe_interpreter; *p != 0; cnt++, p++) if (cnt >= IBE_INTERP_LEN_MAX || !isascii((int)*p)) return (EINVAL); /* Make sure we don't have any invalid #'s. */ p = xbe->xbe_interpreter; - while (1) { - p = strchr(p, '#'); - if (!p) - break; - + interp_offset = 0; + argv0_cnt = 0; + while ((p = strchr(p, '#')) != NULL) { p++; switch(*p) { case ISM_POUND: /* "##" */ p++; + interp_offset--; break; - case ISM_OLD_ARGV0: /* "#a" */ p++; + argv0_cnt++; break; - case 0: default: /* Anything besides the above is invalid. */ return (EINVAL); } } - sx_xlock(&interp_list_sx); + INTERP_LIST_WLOCK(); if (imgact_binmisc_find_entry(xbe->xbe_name) != NULL) { - sx_xunlock(&interp_list_sx); + INTERP_LIST_WUNLOCK(); return (EEXIST); } /* Preallocate a new entry. */ - ibe = imgact_binmisc_new_entry(xbe); + ibe = imgact_binmisc_new_entry(xbe, interp_offset, argv0_cnt); SLIST_INSERT_HEAD(&interpreter_list, ibe, link); interp_list_entry_count++; - sx_xunlock(&interp_list_sx); + INTERP_LIST_WUNLOCK(); return (0); } /* * Remove the interpreter in the list with the given name. Return ENOENT * if not found. */ static int imgact_binmisc_remove_entry(char *name) { imgact_binmisc_entry_t *ibe; - sx_xlock(&interp_list_sx); + INTERP_LIST_WLOCK(); if ((ibe = imgact_binmisc_find_entry(name)) == NULL) { - sx_xunlock(&interp_list_sx); + INTERP_LIST_WUNLOCK(); return (ENOENT); } SLIST_REMOVE(&interpreter_list, ibe, imgact_binmisc_entry, link); interp_list_entry_count--; - sx_xunlock(&interp_list_sx); + INTERP_LIST_WUNLOCK(); imgact_binmisc_destroy_entry(ibe); return (0); } /* * Disable the interpreter in the list with the given name. Return ENOENT * if not found. */ static int imgact_binmisc_disable_entry(char *name) { imgact_binmisc_entry_t *ibe; - sx_xlock(&interp_list_sx); + INTERP_LIST_WLOCK(); if ((ibe = imgact_binmisc_find_entry(name)) == NULL) { - sx_xunlock(&interp_list_sx); + INTERP_LIST_WUNLOCK(); return (ENOENT); } ibe->ibe_flags &= ~IBF_ENABLED; - sx_xunlock(&interp_list_sx); + INTERP_LIST_WUNLOCK(); return (0); } /* * Enable the interpreter in the list with the given name. Return ENOENT * if not found. */ static int imgact_binmisc_enable_entry(char *name) { imgact_binmisc_entry_t *ibe; - sx_xlock(&interp_list_sx); + INTERP_LIST_WLOCK(); if ((ibe = imgact_binmisc_find_entry(name)) == NULL) { - sx_xunlock(&interp_list_sx); + INTERP_LIST_WUNLOCK(); return (ENOENT); } ibe->ibe_flags |= IBF_ENABLED; - sx_xunlock(&interp_list_sx); + INTERP_LIST_WUNLOCK(); return (0); } static int imgact_binmisc_populate_xbe(ximgact_binmisc_entry_t *xbe, imgact_binmisc_entry_t *ibe) { uint32_t i; - sx_assert(&interp_list_sx, SA_LOCKED); - + INTERP_LIST_ASSERT_LOCKED(); memset(xbe, 0, sizeof(*xbe)); strlcpy(xbe->xbe_name, ibe->ibe_name, IBE_NAME_MAX); /* Copy interpreter string. Replace NULL breaks with space. */ memcpy(xbe->xbe_interpreter, ibe->ibe_interpreter, ibe->ibe_interp_length); for(i = 0; i < (ibe->ibe_interp_length - 1); i++) if (xbe->xbe_interpreter[i] == '\0') xbe->xbe_interpreter[i] = ' '; memcpy(xbe->xbe_magic, ibe->ibe_magic, ibe->ibe_msize); memcpy(xbe->xbe_mask, ibe->ibe_mask, ibe->ibe_msize); xbe->xbe_version = IBE_VERSION; xbe->xbe_flags = ibe->ibe_flags; xbe->xbe_moffset = ibe->ibe_moffset; xbe->xbe_msize = ibe->ibe_msize; return (0); } /* * Retrieve the interpreter with the give name and populate the * ximgact_binmisc_entry structure. Return ENOENT if not found. */ static int imgact_binmisc_lookup_entry(char *name, ximgact_binmisc_entry_t *xbe) { imgact_binmisc_entry_t *ibe; int error = 0; - sx_slock(&interp_list_sx); + INTERP_LIST_RLOCK(); if ((ibe = imgact_binmisc_find_entry(name)) == NULL) { - sx_sunlock(&interp_list_sx); + INTERP_LIST_RUNLOCK(); return (ENOENT); } error = imgact_binmisc_populate_xbe(xbe, ibe); - sx_sunlock(&interp_list_sx); + INTERP_LIST_RUNLOCK(); return (error); } /* * Get a snapshot of all the interpreter entries in the list. */ static int imgact_binmisc_get_all_entries(struct sysctl_req *req) { ximgact_binmisc_entry_t *xbe, *xbep; imgact_binmisc_entry_t *ibe; int error = 0, count; - sx_slock(&interp_list_sx); + INTERP_LIST_RLOCK(); count = interp_list_entry_count; xbe = malloc(sizeof(*xbe) * count, M_BINMISC, M_WAITOK|M_ZERO); xbep = xbe; SLIST_FOREACH(ibe, &interpreter_list, link) { error = imgact_binmisc_populate_xbe(xbep++, ibe); if (error) break; } - sx_sunlock(&interp_list_sx); + INTERP_LIST_RUNLOCK(); if (!error) error = SYSCTL_OUT(req, xbe, sizeof(*xbe) * count); free(xbe, M_BINMISC); return (error); } /* * sysctl() handler for munipulating interpretor table. * Not MP safe (locked by sysctl). */ static int sysctl_kern_binmisc(SYSCTL_HANDLER_ARGS) { ximgact_binmisc_entry_t xbe; int error = 0; switch(arg2) { case IBC_ADD: /* Add an entry. Limited to IBE_MAX_ENTRIES. */ error = SYSCTL_IN(req, &xbe, sizeof(xbe)); if (error) return (error); if (IBE_VERSION != xbe.xbe_version) return (EINVAL); + if ((xbe.xbe_flags & ~IBF_VALID_UFLAGS) != 0) + return (EINVAL); if (interp_list_entry_count == IBE_MAX_ENTRIES) return (ENOSPC); error = imgact_binmisc_add_entry(&xbe); break; case IBC_REMOVE: /* Remove an entry. */ error = SYSCTL_IN(req, &xbe, sizeof(xbe)); if (error) return (error); if (IBE_VERSION != xbe.xbe_version) return (EINVAL); error = imgact_binmisc_remove_entry(xbe.xbe_name); break; case IBC_DISABLE: /* Disable an entry. */ error = SYSCTL_IN(req, &xbe, sizeof(xbe)); if (error) return (error); if (IBE_VERSION != xbe.xbe_version) return (EINVAL); error = imgact_binmisc_disable_entry(xbe.xbe_name); break; case IBC_ENABLE: /* Enable an entry. */ error = SYSCTL_IN(req, &xbe, sizeof(xbe)); if (error) return (error); if (IBE_VERSION != xbe.xbe_version) return (EINVAL); error = imgact_binmisc_enable_entry(xbe.xbe_name); break; case IBC_LOOKUP: /* Lookup an entry. */ error = SYSCTL_IN(req, &xbe, sizeof(xbe)); if (error) return (error); if (IBE_VERSION != xbe.xbe_version) return (EINVAL); error = imgact_binmisc_lookup_entry(xbe.xbe_name, &xbe); if (!error) error = SYSCTL_OUT(req, &xbe, sizeof(xbe)); break; case IBC_LIST: /* Return a snapshot of the interpretor list. */ if (!req->oldptr) { /* No pointer then just return the list size. */ error = SYSCTL_OUT(req, 0, interp_list_entry_count * sizeof(ximgact_binmisc_entry_t)); return (error); } else if (!req->oldlen) return (EINVAL); error = imgact_binmisc_get_all_entries(req); break; default: return (EINVAL); } return (error); } SYSCTL_NODE(_kern, OID_AUTO, binmisc, CTLFLAG_RW, 0, "Image activator for miscellaneous binaries"); SYSCTL_PROC(_kern_binmisc, OID_AUTO, add, CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_ADD, sysctl_kern_binmisc, "S,ximgact_binmisc_entry", "Add an activator entry"); SYSCTL_PROC(_kern_binmisc, OID_AUTO, remove, CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_REMOVE, sysctl_kern_binmisc, "S,ximgact_binmisc_entry", "Remove an activator entry"); SYSCTL_PROC(_kern_binmisc, OID_AUTO, disable, CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_DISABLE, sysctl_kern_binmisc, "S,ximgact_binmisc_entry", "Disable an activator entry"); SYSCTL_PROC(_kern_binmisc, OID_AUTO, enable, CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_ENABLE, sysctl_kern_binmisc, "S,ximgact_binmisc_entry", "Enable an activator entry"); SYSCTL_PROC(_kern_binmisc, OID_AUTO, lookup, CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_RW|CTLFLAG_ANYBODY, NULL, IBC_LOOKUP, sysctl_kern_binmisc, "S,ximgact_binmisc_entry", "Lookup an activator entry"); SYSCTL_PROC(_kern_binmisc, OID_AUTO, list, CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_RD|CTLFLAG_ANYBODY, NULL, IBC_LIST, sysctl_kern_binmisc, "S,ximgact_binmisc_entry", "Get snapshot of all the activator entries"); static imgact_binmisc_entry_t * imgact_binmisc_find_interpreter(const char *image_header) { imgact_binmisc_entry_t *ibe; const char *p; int i; size_t sz; - sx_assert(&interp_list_sx, SA_LOCKED); + INTERP_LIST_ASSERT_LOCKED(); SLIST_FOREACH(ibe, &interpreter_list, link) { if (!(IBF_ENABLED & ibe->ibe_flags)) continue; p = image_header + ibe->ibe_moffset; sz = ibe->ibe_msize; if (IBF_USE_MASK & ibe->ibe_flags) { /* Compare using mask. */ for (i = 0; i < sz; i++) if ((*p++ ^ ibe->ibe_magic[i]) & ibe->ibe_mask[i]) break; } else { for (i = 0; i < sz; i++) if (*p++ ^ ibe->ibe_magic[i]) break; } if (i == ibe->ibe_msize) return (ibe); } return (NULL); } static int imgact_binmisc_exec(struct image_params *imgp) { const char *image_header = imgp->image_header; const char *fname = NULL; int error = 0; - size_t offset, l; +#ifdef INVARIANTS + int argv0_cnt = 0; +#endif + size_t namelen, offset; imgact_binmisc_entry_t *ibe; struct sbuf *sname; char *s, *d; + sname = NULL; + namelen = 0; /* Do we have an interpreter for the given image header? */ - sx_slock(&interp_list_sx); + INTERP_LIST_RLOCK(); if ((ibe = imgact_binmisc_find_interpreter(image_header)) == NULL) { - sx_sunlock(&interp_list_sx); - return (-1); + error = -1; + goto done; } /* No interpreter nesting allowed. */ if (imgp->interpreted & IMGACT_BINMISC) { - sx_sunlock(&interp_list_sx); - return (ENOEXEC); + error = ENOEXEC; + goto done; } imgp->interpreted |= IMGACT_BINMISC; - if (imgp->args->fname != NULL) { - fname = imgp->args->fname; - sname = NULL; - } else { - /* Use the fdescfs(5) path for fexecve(2). */ - sname = sbuf_new_auto(); - sbuf_printf(sname, "/dev/fd/%d", imgp->args->fd); - sbuf_finish(sname); - fname = sbuf_data(sname); + /* + * Don't bother with the overhead of putting fname together if we're not + * using #a. + */ + if (ibe->ibe_argv0_cnt != 0) { + if (imgp->args->fname != NULL) { + fname = imgp->args->fname; + } else { + /* Use the fdescfs(5) path for fexecve(2). */ + sname = sbuf_new_auto(); + sbuf_printf(sname, "/dev/fd/%d", imgp->args->fd); + sbuf_finish(sname); + fname = sbuf_data(sname); + } + + namelen = strlen(fname); } /* * We need to "push" the interpreter in the arg[] list. To do this, * we first shift all the other values in the `begin_argv' area to * provide the exact amount of room for the values added. Set up * `offset' as the number of bytes to be added to the `begin_argv' - * area. + * area. ibe_interp_offset is the fixed offset from macros present in + * the interpreter string. */ - offset = ibe->ibe_interp_length; + offset = ibe->ibe_interp_length + ibe->ibe_interp_offset; - /* Adjust the offset for #'s. */ - s = ibe->ibe_interpreter; - while (1) { - s = strchr(s, '#'); - if (!s) - break; + /* Variable offset to be added from macros to the interpreter string. */ + MPASS(ibe->ibe_argv0_cnt == 0 || namelen > 0); + offset += ibe->ibe_argv0_cnt * (namelen - 2); - s++; - switch(*s) { - case ISM_POUND: - /* "##" -> "#": reduce offset by one. */ - offset--; - break; - - case ISM_OLD_ARGV0: - /* "#a" -> (old argv0): increase offset to fit fname */ - offset += strlen(fname) - 2; - break; - - default: - /* Hmm... This shouldn't happen. */ - sx_sunlock(&interp_list_sx); - printf("%s: Unknown macro #%c sequence in " - "interpreter string\n", KMOD_NAME, *(s + 1)); - error = EINVAL; - goto done; - } - s++; - } - /* Check to make sure we won't overrun the stringspace. */ if (offset > imgp->args->stringspace) { - sx_sunlock(&interp_list_sx); error = E2BIG; goto done; } /* Make room for the interpreter */ bcopy(imgp->args->begin_argv, imgp->args->begin_argv + offset, imgp->args->endp - imgp->args->begin_argv); /* Adjust everything by the offset. */ imgp->args->begin_envv += offset; imgp->args->endp += offset; imgp->args->stringspace -= offset; /* Add the new argument(s) in the count. */ imgp->args->argc += ibe->ibe_interp_argcnt; /* * The original arg[] list has been shifted appropriately. Copy in * the interpreter path. */ s = ibe->ibe_interpreter; d = imgp->args->begin_argv; while(*s != '\0') { switch (*s) { case '#': /* Handle "#" in interpreter string. */ s++; switch(*s) { case ISM_POUND: /* "##": Replace with a single '#' */ *d++ = '#'; break; - case ISM_OLD_ARGV0: /* "#a": Replace with old arg0 (fname). */ - if ((l = strlen(fname)) != 0) { - memcpy(d, fname, l); - d += l; - } + MPASS(ibe->ibe_argv0_cnt >= ++argv0_cnt); + memcpy(d, fname, namelen); + d += namelen; break; - default: - /* Shouldn't happen but skip it if it does. */ - break; + __assert_unreachable(); } break; - case ' ': /* Replace space with NUL to separate arguments. */ *d++ = '\0'; break; - default: *d++ = *s; break; } s++; } *d = '\0'; - sx_sunlock(&interp_list_sx); - if (!error) - imgp->interpreter_name = imgp->args->begin_argv; + /* Catch ibe->ibe_argv0_cnt counting more #a than we did. */ + MPASS(ibe->ibe_argv0_cnt == argv0_cnt); + imgp->interpreter_name = imgp->args->begin_argv; done: + INTERP_LIST_RUNLOCK(); if (sname) sbuf_delete(sname); return (error); } static void imgact_binmisc_init(void *arg) { - sx_init(&interp_list_sx, KMOD_NAME); + INTERP_LIST_LOCK_INIT(); } static void imgact_binmisc_fini(void *arg) { imgact_binmisc_entry_t *ibe, *ibe_tmp; /* Free all the interpreters. */ - sx_xlock(&interp_list_sx); + INTERP_LIST_WLOCK(); SLIST_FOREACH_SAFE(ibe, &interpreter_list, link, ibe_tmp) { SLIST_REMOVE(&interpreter_list, ibe, imgact_binmisc_entry, link); imgact_binmisc_destroy_entry(ibe); } - sx_xunlock(&interp_list_sx); + INTERP_LIST_WUNLOCK(); - sx_destroy(&interp_list_sx); + INTERP_LIST_LOCK_DESTROY(); } SYSINIT(imgact_binmisc, SI_SUB_EXEC, SI_ORDER_MIDDLE, imgact_binmisc_init, NULL); SYSUNINIT(imgact_binmisc, SI_SUB_EXEC, SI_ORDER_MIDDLE, imgact_binmisc_fini, NULL); /* * Tell kern_execve.c about it, with a little help from the linker. */ static struct execsw imgact_binmisc_execsw = { .ex_imgact = imgact_binmisc_exec, .ex_name = KMOD_NAME }; EXEC_SET(imgact_binmisc, imgact_binmisc_execsw); Index: stable/12/sys/sys/imgact_binmisc.h =================================================================== --- stable/12/sys/sys/imgact_binmisc.h (revision 367656) +++ stable/12/sys/sys/imgact_binmisc.h (revision 367657) @@ -1,174 +1,181 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 Stacey D. Son * * 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 REGENTS 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 REGENTS 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. * * $FreeBSD$ */ #ifndef _IMGACT_BINMISC_H_ #define _IMGACT_BINMISC_H_ /** * Miscellaneous binary interpreter image activator. */ #include /* for MAXPATHLEN */ /* * Imgact bin misc parameters. */ #define IBE_VERSION 1 /* struct ximgact_binmisc_entry version. */ #define IBE_NAME_MAX 32 /* Max size for entry name. */ #define IBE_MAGIC_MAX 256 /* Max size for header magic and mask. */ #define IBE_ARG_LEN_MAX 256 /* Max space for optional interpreter command- line arguments separated by white space */ #define IBE_INTERP_LEN_MAX (MAXPATHLEN + IBE_ARG_LEN_MAX) #define IBE_MAX_ENTRIES 64 /* Max number of interpreter entries. */ +/* We only map the first page for identification purposes. */ +#define IBE_MATCH_MAX PAGE_SIZE +_Static_assert(IBE_MAGIC_MAX <= IBE_MATCH_MAX, + "Cannot identify binaries past the first page."); + /* * Imgact bin misc interpreter entry flags. */ #define IBF_ENABLED 0x0001 /* Entry is active. */ #define IBF_USE_MASK 0x0002 /* Use mask on header magic field. */ + +#define IBF_VALID_UFLAGS 0x0003 /* Bits allowed from userland. */ /* * Used with sysctlbyname() to pass imgact bin misc entries in and out of the * kernel. */ typedef struct ximgact_binmisc_entry { uint32_t xbe_version; /* Struct version(IBE_VERSION) */ uint32_t xbe_flags; /* Entry flags (IBF_*) */ uint32_t xbe_moffset; /* Magic offset in header */ uint32_t xbe_msize; /* Magic size */ uint32_t spare[3]; /* Spare fields for future use */ char xbe_name[IBE_NAME_MAX]; /* Unique interpreter name */ char xbe_interpreter[IBE_INTERP_LEN_MAX]; /* Interpreter path + args */ uint8_t xbe_magic[IBE_MAGIC_MAX]; /* Header Magic */ uint8_t xbe_mask[IBE_MAGIC_MAX]; /* Magic Mask */ } ximgact_binmisc_entry_t; /* * sysctl() command names. */ #define IBE_SYSCTL_NAME "kern.binmisc" #define IBE_SYSCTL_NAME_ADD IBE_SYSCTL_NAME ".add" #define IBE_SYSCTL_NAME_REMOVE IBE_SYSCTL_NAME ".remove" #define IBE_SYSCTL_NAME_DISABLE IBE_SYSCTL_NAME ".disable" #define IBE_SYSCTL_NAME_ENABLE IBE_SYSCTL_NAME ".enable" #define IBE_SYSCTL_NAME_LOOKUP IBE_SYSCTL_NAME ".lookup" #define IBE_SYSCTL_NAME_LIST IBE_SYSCTL_NAME ".list" #define KMOD_NAME "imgact_binmisc" /* * Examples of manipulating the interpreter table using sysctlbyname(3): * * #include * * #define LLVM_MAGIC "BC\xc0\xde" * * #define MIPS64_ELF_MAGIC "\x7f\x45\x4c\x46\x02\x02\x01\x00\x00\x00" \ * "\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08" * #define MIPS64_ELF_MASK "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff" \ * "\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff" * * ximgact_binmisc_entry_t xbe, *xbep, out_xbe; * size_t size = 0, osize; * int error, i; * * // Add image activator for LLVM byte code * bzero(&xbe, sizeof(xbe)); * xbe.xbe_version = IBE_VERSION; * xbe.xbe_flags = IBF_ENABLED; * strlcpy(xbe.xbe_name, "llvm_bc", IBE_NAME_MAX); * strlcpy(xbe.xbe_interpreter, "/usr/bin/lli --fake-arg0=#a", * IBE_INTERP_LEN_MAX); * xbe.xbe_moffset = 0; * xbe.xbe_msize = 4; * memcpy(xbe.xbe_magic, LLVM_MAGIC, xbe.xbe_msize); * error = sysctlbyname(IBE_SYSCTL_NAME_ADD, NULL, NULL, &xbe, sizeof(xbe)); * * // Add image activator for mips64 ELF binaries to use qemu user mode * bzero(&xbe, sizeof(xbe)); * xbe.xbe_version = IBE_VERSION; * xbe.xbe_flags = IBF_ENABLED | IBF_USE_MASK; * strlcpy(xbe.xbe_name, "mips64elf", IBE_NAME_MAX); * strlcpy(xbe.xbe_interpreter, "/usr/local/bin/qemu-mips64", * IBE_INTERP_LEN_MAX); * xbe.xbe_moffset = 0; * xbe.xbe_msize = 20; * memcpy(xbe.xbe_magic, MIPS64_ELF_MAGIC, xbe.xbe_msize); * memcpy(xbe.xbe_mask, MIPS64_ELF_MASK, xbe.xbe_msize); * sysctlbyname(IBE_SYSCTL_NAME_ADD, NULL, NULL, &xbe, sizeof(xbe)); * * // Disable (OR Enable OR Remove) image activator for LLVM byte code * bzero(&xbe, sizeof(xbe)); * xbe.xbe_version = IBE_VERSION; * strlcpy(xbe.xbe_name, "llvm_bc", IBE_NAME_MAX); * error = sysctlbyname(IBE_SYSCTL_NAME_DISABLE, NULL, NULL, &xbe, sizeof(xbe)); * // OR sysctlbyname(IBE_SYSCTL_NAME_ENABLE, NULL, NULL, &xbe, sizeof(xbe)); * // OR sysctlbyname(IBE_SYSCTL_NAME_REMOVE, NULL, NULL, &xbe, sizeof(xbe)); * * // Lookup image activator "llvm_bc" * bzero(&xbe, sizeof(xbe)); * xbe.xbe_version = IBE_VERSION; * strlcpy(xbe.xbe_name, "llvm_bc", IBE_NAME_MAX); * size = sizeof(out_xbe); * error = sysctlbyname(IBE_SYSCTL_NAME_LOOKUP, &out_xbe, &size, &xbe, * sizeof(xbe)); * * // Get all the currently configured image activators and report * error = sysctlbyname(IBE_SYSCTL_NAME_LIST, NULL, &size, NULL, 0); * if (0 == error && size > 0) { * xbep = malloc(size); * while(1) { * osize = size; * error = sysctlbyname("kern.binmisc.list", xbep, &size, NULL, 0); * if (-1 == error && ENOMEM == errno && size == osize) { * // The buffer too small and needs to grow * size += sizeof(xbe); * xbep = realloc(xbep, size); * } else * break; * } * } * for(i = 0; i < (size / sizeof(xbe)); i++, xbep++) * printf("name: %s interpreter: %s flags: %s %s\n", xbep->xbe_name, * xbep->xbe_interpreter, (xbep->xbe_flags & IBF_ENABLED) ? * "ENABLED" : "", (xbep->xbe_flags & IBF_ENABLED) ? "USE_MASK" : ""); * * The sysctlbyname() calls above may return the following errors in addition * to the standard ones: * * [EINVAL] Invalid argument in the input ximgact_binmisc_entry_t structure. * [EEXIST] Interpreter entry for given name already exist in kernel list. * [ENOMEM] Allocating memory in the kernel failed or, in the case of * kern.binmisc.list, the user buffer is too small. * [ENOENT] Interpreter entry for given name is not found. * [ENOSPC] Attempted to exceed maximum number of entries (IBE_MAX_ENTRIES). */ #endif /* !_IMGACT_BINMISC_H_ */ Index: stable/12/usr.sbin/binmiscctl/Makefile =================================================================== --- stable/12/usr.sbin/binmiscctl/Makefile (revision 367656) +++ stable/12/usr.sbin/binmiscctl/Makefile (revision 367657) @@ -1,10 +1,10 @@ # # $FreeBSD$ # .include - + PROG= binmiscctl MAN= binmiscctl.8 .include Index: stable/12/usr.sbin/binmiscctl/binmiscctl.c =================================================================== --- stable/12/usr.sbin/binmiscctl/binmiscctl.c (revision 367656) +++ stable/12/usr.sbin/binmiscctl/binmiscctl.c (revision 367657) @@ -1,513 +1,513 @@ /*- * Copyright (c) 2013 Stacey D. Son * 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 AUTHOR 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 AUTHOR 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. * */ #include __FBSDID("$FreeBSD$"); +#include +#include +#include +#include +#include + #include #include #include #include #include #include #include #include -#include -#include -#include -#include -#include - enum cmd { CMD_ADD = 0, CMD_REMOVE, CMD_DISABLE, CMD_ENABLE, CMD_LOOKUP, CMD_LIST, }; extern char *__progname; typedef int (*cmd_func_t)(int argc, char *argv[], ximgact_binmisc_entry_t *xbe); int add_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe); int name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe); int noname_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe); static const struct { const int token; const char *name; cmd_func_t func; const char *desc; const char *args; } cmds[] = { { CMD_ADD, "add", add_cmd, "Add a new binary image activator (requires 'root' privilege)", " --interpreter \\\n" "\t\t--magic [--mask ] \\\n" "\t\t--size [--offset ] \\\n" "\t\t[--set-enabled]" }, { CMD_REMOVE, "remove", name_cmd, "Remove a binary image activator (requires 'root' privilege)", "" }, { CMD_DISABLE, "disable", name_cmd, "Disable a binary image activator (requires 'root' privilege)", "" }, { CMD_ENABLE, "enable", name_cmd, "Enable a binary image activator (requires 'root' privilege)", "" }, { CMD_LOOKUP, "lookup", name_cmd, "Lookup a binary image activator", "" }, { CMD_LIST, "list", noname_cmd, "List all the binary image activators", "" }, }; static const struct option add_opts[] = { { "set-enabled", no_argument, NULL, 'e' }, { "interpreter", required_argument, NULL, 'i' }, { "mask", required_argument, NULL, 'M' }, { "magic", required_argument, NULL, 'm' }, { "offset", required_argument, NULL, 'o' }, { "size", required_argument, NULL, 's' }, { NULL, 0, NULL, 0 } }; static char const *cmd_sysctl_name[] = { IBE_SYSCTL_NAME_ADD, IBE_SYSCTL_NAME_REMOVE, IBE_SYSCTL_NAME_DISABLE, IBE_SYSCTL_NAME_ENABLE, IBE_SYSCTL_NAME_LOOKUP, IBE_SYSCTL_NAME_LIST }; -static void +static void __dead2 usage(const char *format, ...) { va_list args; size_t i; int error = 0; va_start(args, format); if (format) { vfprintf(stderr, format, args); error = -1; } va_end(args); fprintf(stderr, "\n"); fprintf(stderr, "usage: %s command [args...]\n\n", __progname); - for(i = 0; i < ( sizeof (cmds) / sizeof (cmds[0])); i++) { + for(i = 0; i < nitems(cmds); i++) { fprintf(stderr, "%s:\n", cmds[i].desc); fprintf(stderr, "\t%s %s %s\n\n", __progname, cmds[i].name, cmds[i].args); } exit (error); } -static void +static void __dead2 fatal(const char *format, ...) { va_list args; va_start(args, format); if (format) vfprintf(stderr, format, args); fprintf(stderr, "\n"); exit(-1); } static void getoptstr(char *str, size_t size, const char *argname) { if (strlen(optarg) > size) usage("'%s' too large", argname); strlcpy(str, optarg, size); } static void printxbe(ximgact_binmisc_entry_t *xbe) { uint32_t i, flags = xbe->xbe_flags; if (xbe->xbe_version != IBE_VERSION) { fprintf(stderr, "Error: XBE version mismatch\n"); return; } printf("name: %s\n", xbe->xbe_name); printf("interpreter: %s\n", xbe->xbe_interpreter); printf("flags: %s%s\n", (flags & IBF_ENABLED) ? "ENABLED " : "", (flags & IBF_USE_MASK) ? "USE_MASK " : ""); printf("magic size: %u\n", xbe->xbe_msize); printf("magic offset: %u\n", xbe->xbe_moffset); printf("magic: "); for(i = 0; i < xbe->xbe_msize; i++) { if (i && !(i % 12)) printf("\n "); else if (i && !(i % 4)) printf(" "); printf("0x%02x ", xbe->xbe_magic[i]); } printf("\n"); if (flags & IBF_USE_MASK) { printf("mask: "); for(i = 0; i < xbe->xbe_msize; i++) { if (i && !(i % 12)) printf("\n "); else if (i && !(i % 4)) printf(" "); printf("0x%02x ", xbe->xbe_mask[i]); } printf("\n"); } printf("\n"); } static int demux_cmd(__unused int argc, char *const argv[]) { size_t i; optind = 1; optreset = 1; - for(i = 0; i < ( sizeof (cmds) / sizeof (cmds[0])); i++) { + for(i = 0; i < nitems(cmds); i++) { if (!strcasecmp(cmds[i].name, argv[0])) { return (i); } } /* Unknown command */ return (-1); } static int strlit2bin_cpy(uint8_t *d, char *s, size_t size) { int c; size_t cnt = 0; while((c = *s++) != '\0') { if (c == '\\') { /* Do '\' escapes. */ switch (*s) { case '\\': *d++ = '\\'; break; case 'x': s++; c = toupper(*s++); *d = (c - (isdigit(c) ? '0' : ('A' - 10))) << 4; c = toupper(*s++); *d++ |= c - (isdigit(c) ? '0' : ('A' - 10)); break; default: return (-1); } } else *d++ = c; if (++cnt > size) return (-1); } return (cnt); } int add_cmd(__unused int argc, char *argv[], ximgact_binmisc_entry_t *xbe) { int ch; char *magic = NULL, *mask = NULL; int sz; if (strlen(argv[0]) > IBE_NAME_MAX) usage("'%s' string length longer than IBE_NAME_MAX (%d)", IBE_NAME_MAX); strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX); while ((ch = getopt_long(argc, argv, "ei:m:M:o:s:", add_opts, NULL)) != -1) { switch(ch) { case 'i': getoptstr(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX, "interpreter"); break; case 'm': free(magic); magic = strdup(optarg); break; case 'M': free(mask); mask = strdup(optarg); xbe->xbe_flags |= IBF_USE_MASK; break; case 'e': xbe->xbe_flags |= IBF_ENABLED; break; case 'o': xbe->xbe_moffset = atol(optarg); break; case 's': xbe->xbe_msize = atol(optarg); if (xbe->xbe_msize == 0 || xbe->xbe_msize > IBE_MAGIC_MAX) usage("Error: Not valid '--size' value. " "(Must be > 0 and < %u.)\n", xbe->xbe_msize); break; default: usage("Unknown argument: '%c'", ch); } } if (xbe->xbe_msize == 0) { if (NULL != magic) free(magic); if (NULL != mask) free(mask); usage("Error: Missing '--size' argument"); } if (NULL != magic) { if (xbe->xbe_msize == 0) { if (magic) free(magic); if (mask) free(mask); usage("Error: Missing magic size argument"); } sz = strlit2bin_cpy(xbe->xbe_magic, magic, IBE_MAGIC_MAX); free(magic); if (sz == -1 || (uint32_t)sz != xbe->xbe_msize) { if (mask) free(mask); usage("Error: invalid magic argument"); } if (mask) { sz = strlit2bin_cpy(xbe->xbe_mask, mask, IBE_MAGIC_MAX); free(mask); if (sz == -1 || (uint32_t)sz != xbe->xbe_msize) usage("Error: invalid mask argument"); } } else { if (mask) free(mask); usage("Error: Missing magic argument"); } if (!strnlen(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX)) { usage("Error: Missing 'interpreter' argument"); } return (0); } int name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe) { if (argc == 0) usage("Required argument missing\n"); if (strlen(argv[0]) > IBE_NAME_MAX) usage("'%s' string length longer than IBE_NAME_MAX (%d)", IBE_NAME_MAX); strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX); return (0); } int noname_cmd(__unused int argc, __unused char *argv[], __unused ximgact_binmisc_entry_t *xbe) { return (0); } int main(int argc, char **argv) { int error = 0, cmd = -1; ximgact_binmisc_entry_t xbe_in, *xbe_inp = NULL; ximgact_binmisc_entry_t xbe_out, *xbe_outp = NULL; size_t xbe_in_sz = 0; size_t xbe_out_sz = 0, *xbe_out_szp = NULL; uint32_t i; if (modfind(KMOD_NAME) == -1) { if (kldload(KMOD_NAME) == -1) fatal("Can't load %s kernel module: %s", KMOD_NAME, strerror(errno)); } bzero(&xbe_in, sizeof(xbe_in)); bzero(&xbe_out, sizeof(xbe_out)); xbe_in.xbe_version = IBE_VERSION; if (argc < 2) usage("Error: requires at least one argument"); argc--, argv++; cmd = demux_cmd(argc, argv); if (cmd < 0) usage("Error: Unknown command \"%s\"", argv[0]); argc--, argv++; error = (*cmds[cmd].func)(argc, argv, &xbe_in); if (error) usage("Can't parse command-line for '%s' command", cmds[cmd].name); if (cmd != CMD_LIST) { xbe_inp = &xbe_in; xbe_in_sz = sizeof(xbe_in); } else xbe_out_szp = &xbe_out_sz; if (cmd == CMD_LOOKUP) { xbe_out_sz = sizeof(xbe_out); xbe_outp = &xbe_out; xbe_out_szp = &xbe_out_sz; } error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp, xbe_out_szp, xbe_inp, xbe_in_sz); if (error) switch(errno) { case EINVAL: usage("Invalid interpreter name or --interpreter, " "--magic, --mask, or --size argument value"); break; case EEXIST: usage("'%s' is not unique in activator list", xbe_in.xbe_name); break; case ENOENT: usage("'%s' is not found in activator list", xbe_in.xbe_name); break; case ENOSPC: fatal("Fatal: no more room in the activator list " "(limited to %d enties)", IBE_MAX_ENTRIES); break; case EPERM: usage("Insufficient privileges for '%s' command", cmds[cmd].name); break; default: fatal("Fatal: sysctlbyname() returned: %s", strerror(errno)); break; } if (cmd == CMD_LOOKUP) printxbe(xbe_outp); if (cmd == CMD_LIST && xbe_out_sz > 0) { xbe_outp = malloc(xbe_out_sz); if (!xbe_outp) fatal("Fatal: out of memory"); while(1) { size_t osize = xbe_out_sz; error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp, &xbe_out_sz, NULL, 0); if (error == -1 && errno == ENOMEM && xbe_out_sz == osize) { /* * Buffer too small. Increase it by one * entry. */ xbe_out_sz += sizeof(xbe_out); xbe_outp = realloc(xbe_outp, xbe_out_sz); if (!xbe_outp) fatal("Fatal: out of memory"); } else break; } if (error) { free(xbe_outp); fatal("Fatal: %s", strerror(errno)); } - for(i = 0; i < (xbe_out_sz / sizeof(xbe_out)); i++) + for(i = 0; i < howmany(xbe_out_sz, sizeof(xbe_out)); i++) printxbe(&xbe_outp[i]); } return (error); } Index: stable/12 =================================================================== --- stable/12 (revision 367656) +++ stable/12 (revision 367657) Property changes on: stable/12 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r367361,367439,367441-367442,367444,367452,367456,367477