Index: lib/libc/gen/getcap.c =================================================================== --- lib/libc/gen/getcap.c (revision 274808) +++ lib/libc/gen/getcap.c (working copy) @@ -32,20 +32,21 @@ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)getcap.c 8.3 (Berkeley) 3/25/94"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include +#include #include #include #include #include #include #include #include #include #include "un-namespace.h" @@ -57,25 +58,36 @@ __FBSDID("$FreeBSD$"); #define MAX_RECURSION 32 /* maximum getent recursion */ #define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */ #define RECOK (char)0 #define TCERR (char)1 #define SHADOW (char)2 static size_t topreclen; /* toprec length */ static char *toprec; /* Additional record specified by cgetset() */ static int gottoprec; /* Flag indicating retrieval of toprecord */ +static void **allocs; /* Array of allocations to free in cgetclose */ +static int nallocs; /* Number of allocs */ static int cdbget(DB *, char **, const char *); static int getent(char **, u_int *, char **, int, const char *, int, char *); static int nfcmp(char *, char *); +static void +cgetregisteralloc(void *alloc) +{ + nallocs++; + allocs = realloc(allocs, nallocs * sizeof(allocs[0])); + assert(allocs != NULL); + allocs[nallocs - 1] = alloc; +} + /* * Cgetset() allows the addition of a user specified buffer to be added * to the database array, in effect "pushing" the buffer on top of the * virtual database. 0 is returned on success, -1 on failure. */ int cgetset(const char *ent) { if (ent == NULL) { if (toprec) @@ -517,20 +529,21 @@ tc_exp: { if (myfd) (void)_close(fd); *len = rp - record - 1; /* don't count NUL */ if (r_end > rp) if ((record = reallocf(record, (size_t)(rp - record))) == NULL) { errno = ENOMEM; return (-2); } + cgetregisteralloc(record); *cap = record; if (tc_not_resolved) return (1); return (0); } static int cdbget(DB *capdbp, char **bp, const char *name) { DBT key, data; @@ -627,20 +640,30 @@ static char **dbp; int cgetclose(void) { if (pfp != NULL) { (void)fclose(pfp); pfp = NULL; } dbp = NULL; gottoprec = 0; + if (toprec != NULL) { + free(toprec); + toprec = NULL; + } + if (allocs != NULL) { + while (nallocs-- > 0) + free(allocs[nallocs]); + free(allocs); + allocs = NULL; + } slash = 0; return(0); } /* * Cgetnext() gets either the first or next entry in the logical database * specified by db_array. It returns 0 upon completion of the database, 1 * upon returning an entry with more remaining, and -1 if an error occurs. */ int @@ -886,20 +909,21 @@ cgetstr(char *buf, const char *cap, char *mp++ = '\0'; /* loop invariant let's us do this */ m_room--; len = mp - mem - 1; /* * Give back any extra memory and return value and success. */ if (m_room != 0) if ((mem = reallocf(mem, (size_t)(mp - mem))) == NULL) return (-2); + cgetregisteralloc(mem); *str = mem; return (len); } /* * Cgetustr retrieves the value of the string capability cap from the * capability record pointed to by buf. The difference between cgetustr() * and cgetstr() is that cgetustr does not decode escapes but rather treats * all characters literally. A pointer to a NUL terminated malloc'd * copy of the string is returned in the char pointed to by str. The @@ -958,20 +982,21 @@ cgetustr(char *buf, const char *cap, cha *mp++ = '\0'; /* loop invariant let's us do this */ m_room--; len = mp - mem - 1; /* * Give back any extra memory and return value and success. */ if (m_room != 0) if ((mem = reallocf(mem, (size_t)(mp - mem))) == NULL) return (-2); + cgetregisteralloc(mem); *str = mem; return (len); } /* * Cgetnum retrieves the value of the numeric capability cap from the * capability record pointed to by buf. The numeric value is returned in * the long pointed to by num. 0 is returned on success, -1 if the requested * numeric capability couldn't be found. */ Index: lib/libc/tests/gen/Makefile =================================================================== --- lib/libc/tests/gen/Makefile (revision 274808) +++ lib/libc/tests/gen/Makefile (working copy) @@ -1,18 +1,19 @@ # $FreeBSD$ .include TESTSDIR= ${TESTSBASE}/lib/libc/gen ATF_TESTS_C= arc4random_test ATF_TESTS_C+= fpclassify2_test +ATF_TESTS_C+= getcap_test # TODO: t_closefrom, t_cpuset, t_fmtcheck, t_randomid, t_sleep # TODO: t_siginfo (fixes require further inspection) # TODO: t_sethostname_test (consistently screws up the hostname) NETBSD_ATF_TESTS_C= alarm_test NETBSD_ATF_TESTS_C+= assert_test NETBSD_ATF_TESTS_C+= basedirname_test NETBSD_ATF_TESTS_C+= dir_test NETBSD_ATF_TESTS_C+= floatunditf_test Index: lib/libc/tests/gen/getcap_test.c =================================================================== --- lib/libc/tests/gen/getcap_test.c (revision 0) +++ lib/libc/tests/gen/getcap_test.c (working copy) @@ -0,0 +1,115 @@ +/*- + * Copyright (c) 2014 EMC / Isilon Storage Division + * 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. + */ + +/* + * Checks for memory leaks in cgetstr() (Bug 195128) + * + * Requirements: + * /etc/login.conf with default capability with valid path record + * jemalloc + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +ATF_TC(cgetstr_leak); +ATF_TC_HEAD(cgetstr_leak, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "Check cgetstr(3) for memory leakage (Bug 195128)"); +} + +void +malgetuint64(char *name, uint64_t *val) +{ + size_t len; + + len = sizeof(*val); + if (mallctl(name, val, &len, NULL, 0) == -1) + err(2, "mallctl failed"); + if (len != sizeof(*val)) + err(2, "bad size from mallctl"); +} + +int64_t +getalloc(void) +{ + uint64_t allocated, deallocated; + + malgetuint64("thread.allocated", &allocated); + malgetuint64("thread.deallocated", &deallocated); + + return ((int64_t)(allocated - deallocated)); +} + +ATF_TC_BODY(cgetstr_leak, tc) +{ + char *buf, *str, *dbarray[] = { _PATH_LOGIN_CONF }; + int errcode, i; + int64_t allocated, start, delta; + + start = getalloc(); + + i = cgetent(&buf, dbarray, "default"); + if (i == -1) + atf_tc_skip("Unable to find default class in " _PATH_LOGIN_CONF); + if (i == -2) + atf_tc_skip("Error reading login.conf"); + + allocated = getalloc(); + for (i = 0; i < 1000; i++) { + if ((errcode = cgetstr(buf, "path", &str)) < 0) + atf_tc_skip("cgetstr failed with %d", errcode); + } + allocated = getalloc(); + free(buf); + cgetclose(); + + allocated = getalloc(); + delta = allocated - start; + if (delta > 0) + atf_tc_fail("Failure: leaked %jd bytes", delta); + else + (void)printf("OK: leaked %jd bytes\n", delta); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, cgetstr_leak); + + return atf_no_error(); +} Property changes on: lib/libc/tests/gen/getcap_test.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property