Hi, On 2009-12-15, we fixed a memory leak in the fprintf replacement. Since it is quite important to not reintroduce this bug in the future, I'm adding a unit test against it.
> - Limit the allowed total memory through a setrlimit call > (cf. tests/test-printf-posix2.c). > - Run the test in a loop. Determine the iteration count so that when > the bug is present, the memory would exceed the limit. Of course, > also consider the running time of the test when the bug is fixed. 2009-12-19 Bruno Haible <br...@clisp.org> fprintf-posix: Check against memory leak fixed on 2009-12-15. * tests/test-fprintf-posix3.sh: New file. * tests/test-fprintf-posix3.c: New file. * modules/fprintf-posix-tests (Files): Add them. (Makefile.am): Augment TESTS and CHECK_PROGRAMS. ======================== tests/test-fprintf-posix3.sh ======================== #!/bin/sh # Test against a memory leak. (./test-fprintf-posix3${EXEEXT} 0 result=$? if test $result != 77 && test $result != 78; then result=1; fi exit $result ) 2>/dev/null malloc_result=$? if test $malloc_result = 77; then echo "Skipping test: getrlimit and setrlimit don't work" exit 77 fi ./test-fprintf-posix3${EXEEXT} 1 > /dev/null result=$? if test $result = 77; then echo "Skipping test: getrlimit and setrlimit don't work" exit 77 fi if test $result != 0; then exit 1 fi if test $malloc_result = 78; then echo "Skipping test: getrlimit and setrlimit don't work" exit 77 fi exit 0 ========================= tests/test-fprintf-posix3.c ========================= /* Test of POSIX compatible fprintf() function. Copyright (C) 2009 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* Written by Bruno Haible <br...@clisp.org>, 2009. */ #include <config.h> #include <stdio.h> #if HAVE_GETRLIMIT && HAVE_SETRLIMIT #include <stdlib.h> #include <sys/types.h> #include <sys/time.h> #include <sys/resource.h> #include <string.h> #include <errno.h> /* Test against a memory leak in the fprintf replacement. */ /* Number of iterations across the loop. */ #define NUM_ROUNDS 1000 /* Number of bytes that are allowed to escape per round. */ #define MAX_ALLOC_ROUND 10000 /* Number of bytes that are allowed to escape in total. This should be at least 10 MB, since it includes the normal memory or address space of the test program. */ #define MAX_ALLOC_TOTAL (NUM_ROUNDS * MAX_ALLOC_ROUND) int main (int argc, char *argv[]) { struct rlimit limit; int arg; int repeat; /* Limit the amount of malloc()ed memory to MAX_ALLOC_TOTAL or less. */ /* On BSD systems, malloc() is limited by RLIMIT_DATA. */ #ifdef RLIMIT_DATA if (getrlimit (RLIMIT_DATA, &limit) < 0) return 77; if (limit.rlim_max == RLIM_INFINITY || limit.rlim_max > MAX_ALLOC_TOTAL) limit.rlim_max = MAX_ALLOC_TOTAL; limit.rlim_cur = limit.rlim_max; if (setrlimit (RLIMIT_DATA, &limit) < 0) return 77; #endif /* On Linux systems, malloc() is limited by RLIMIT_AS. */ #ifdef RLIMIT_AS if (getrlimit (RLIMIT_AS, &limit) < 0) return 77; if (limit.rlim_max == RLIM_INFINITY || limit.rlim_max > MAX_ALLOC_TOTAL) limit.rlim_max = MAX_ALLOC_TOTAL; limit.rlim_cur = limit.rlim_max; if (setrlimit (RLIMIT_AS, &limit) < 0) return 77; #endif arg = atoi (argv[1]); if (arg == 0) { void *memory = malloc (MAX_ALLOC_TOTAL); if (memory == NULL) return 1; memset (memory, 17, MAX_ALLOC_TOTAL); return 78; } /* Perform the test and test whether it triggers a permanent memory allocation of more than MAX_ALLOC_TOTAL bytes. */ for (repeat = 0; repeat < NUM_ROUNDS; repeat++) { /* This may produce a temporary memory allocation of 11000 bytes. but should not result in a permanent memory allocation. */ if (fprintf (stdout, "%011000d\n", 17) == -1 && errno == ENOMEM) return 1; } return 0; } #else int main (int argc, char *argv[]) { return 77; } #endif =============================================================================== --- modules/fprintf-posix-tests.orig 2009-12-19 22:09:44.000000000 +0100 +++ modules/fprintf-posix-tests 2009-12-19 21:04:03.000000000 +0100 @@ -5,6 +5,8 @@ tests/test-printf-posix.output tests/test-fprintf-posix2.sh tests/test-fprintf-posix2.c +tests/test-fprintf-posix3.sh +tests/test-fprintf-posix3.c Depends-on: stdint @@ -13,6 +15,6 @@ AC_CHECK_FUNCS_ONCE([getrlimit setrlimit]) Makefile.am: -TESTS += test-fprintf-posix.sh test-fprintf-posix2.sh +TESTS += test-fprintf-posix.sh test-fprintf-posix2.sh test-fprintf-posix3.sh TESTS_ENVIRONMENT += EXEEXT='@EXEEXT@' srcdir='$(srcdir)' -check_PROGRAMS += test-fprintf-posix test-fprintf-posix2 +check_PROGRAMS += test-fprintf-posix test-fprintf-posix2 test-fprintf-posix3