NetBSD Problem Report #41837

From www@NetBSD.org  Fri Aug  7 08:04:51 2009
Return-Path: <www@NetBSD.org>
Received: from mail.netbsd.org (mail.netbsd.org [204.152.190.11])
	by www.NetBSD.org (Postfix) with ESMTP id 6975863C270
	for <gnats-bugs@gnats.netbsd.org>; Fri,  7 Aug 2009 08:04:51 +0000 (UTC)
Message-Id: <20090807080451.2961363BDF9@www.NetBSD.org>
Date: Fri,  7 Aug 2009 08:04:51 +0000 (UTC)
From: ekamperi@gmail.com
Reply-To: ekamperi@gmail.com
To: gnats-bugs@NetBSD.org
Subject: tempnam(3) doesn't return at least {TMP_MAX} unique file paths
X-Send-Pr-Version: www-1.0

>Number:         41837
>Category:       lib
>Synopsis:       tempnam(3) doesn't return at least {TMP_MAX} unique file paths
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Aug 07 08:05:00 +0000 2009
>Last-Modified:  Tue May 03 08:39:51 +0000 2011
>Originator:     Stathis Kamperis
>Release:        NetBSD 5.0_STABLE
>Organization:
Aristotle University of Thessaloniki
>Environment:
NetBSD voyager 5.0_STABLE NetBSD 5.0_STABLE (MYGENERIC) #9: Wed Aug  5 00:38:20 EEST 2009  root@voyager:/usr/obj/sys/arch/i386/compile/MYGENERIC i386

>Description:
First of all, I know that tempnam(3) is of little use nowadays.
Second, the bug I'm going to describe is even rarer to cause any problems.
Thirt, Issue 7 says that tempnam() might be removed in future versions of the standard.
That's why I flagged the PR as non-critical/low.

Anyway.

tempnam(3) doesn't return {TMP_MAX} unique file paths during the lifetime
of a process. Instead it starts repeating itself with a period of = 676
= 26*26. This is because it appends 'aa' to a constant string (that is different from process to process, but stays constant during the lifecycle of a single process) to 'zz'.

As I comment in the code of the test case, it isn't very clear whether
tempnam(3) should always guarantee {TMP_MAX} file paths.

To quote Issue 7:
Some implementations of tempnam() may use tmpnam() internally. On such implementations, if called more than {TMP_MAX} times in a single process, the behavior is implementation-defined.

So, if tempnam(3) uses tmpnamp() internally, then it will at least
return {TMP_MAX} unique strings. But what about in the case that it
doesn't use tmpnam() ? 

Given that tmpfile() and tmpnam() share this {TMP_MAX} limit and tempnam() adheres to it in the case where it makes use of tmpnam(),
it is reasonable to deduce that it should honour the limit even in
the case it isn't implemented on top of tmpnam().

Just for the record, Linux, OpenBSD and Solaris 10 get it right.

>How-To-Repeat:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>     /* strcmp() */

int main(void)
{
        /*
         * If TMP_MAX is defined, use that unless it exceeds 2000.
         * We do this because the implementation may support some
         * astronomically large value and blow up the memory limits.
         * E.g., in DragonFly it equals 308915776. But, even if
         * TMP_MAX isn't that large, it still needs to be kept low,
         * because later on when we check if the generated strings are
         * unique, we use a nested loop which is of O(n^2) complexity.
         * And we want the test to complete in reasonable amount of time.
         */
#ifdef TMP_MAX
        size_t N = TMP_MAX > 2000 ? 2000 : TMP_MAX;
#else
        size_t N = 25;          /* Lowest possible value for TMP_MAX. */
#endif
        char **files;
        size_t i, j;

        /*
         * Allocate memory for file paths.
         * We could use a variable-sized array, but let's stick for now
         * 
>Fix:

>Release-Note:

>Audit-Trail:
From: Stathis Kamperis <ekamperi@gmail.com>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: bin/41837
Date: Sat, 8 Aug 2009 22:52:38 +0300

 #include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>     /* strcmp() */

 int main(void)
 {
        /*
         * If TMP_MAX is defined, use that unless it exceeds 2000.
         * We do this because the implementation may support some
         * astronomically large value and blow up the memory limits.
         * E.g., in DragonFly it equals 308915776. But, even if
         * TMP_MAX isn't that large, it still needs to be kept low,
         * because later on when we check if the generated strings are
         * unique, we use a nested loop which is of O(n^2) complexity.
         * And we want the test to complete in reasonable amount of time.
         */
 #ifdef TMP_MAX
        size_t N = TMP_MAX > 2000 ? 2000 : TMP_MAX;
 #else
        size_t N = 25;          /* Lowest possible value for TMP_MAX. */
 #endif
        char **files;
        size_t i, j;

        /*
         * Allocate memory for file paths.
         * We could use a variable-sized array, but let's stick for now
         * to the C89.
         */
        files = malloc(N * sizeof(char *));
        assert(files != NULL);

        /* Generate the file paths. */
        for (i = 0; i < N; i++) {
                files[i] = tempnam("/tmp", "file");
                assert(files[i] != NULL);
        }

        /*
         * Make sure that all generated paths start with our directory,
         * and contain our prefix.
         */
        for (i = 0; i < N; i++) {
                assert(strncmp(files[i], "/tmp/", 5) == 0);
                assert(strncmp(files[i] + 5, "file", 4) == 0);
        }

        /*
         * If the implementation uses tmpnam() internally, we are still
         * guaranteed at least {TMP_MAX} unique filenames. But, to be
         * honest, there's no explicit statement for the case where
         * tempnam() doesn't use tmpnam(). Anyway, tmpfile() shares the
         * same limit as tmpnam(), so it's reasonable to assume that
         * tempnam() does the same as well.
         */
        for (i = 0; i < N-1; i++)
                for (j = i+1; j < N; j++)
                        assert(strcmp(files[i], files[j]) != 0);

        /* We are done -- free resouces. */
        for (i = 0; i < N; i++)
                free(files[i]);
        free(files);

        printf("passed\n");

        return (EXIT_SUCCESS);
 }

Responsible-Changed-From-To: bin-bug-people->lib-bug-people
Responsible-Changed-By: jruoho@NetBSD.org
Responsible-Changed-When: Tue, 03 May 2011 08:39:51 +0000
Responsible-Changed-Why:

Change category to "lib".



>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2007 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.