[Bug 268479] lib/libc/stdlib/getenv.c may have a problem with putenv()

[email protected]
Newsgroups gmane.os.freebsd.devel.standards
Message-ID <[email protected]/bugzilla/>
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=268479

            Bug ID: 268479
           Summary: lib/libc/stdlib/getenv.c may have a problem with
                    putenv()
           Product: Base System
           Version: CURRENT
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Only Me
          Priority: ---
         Component: standards
          Assignee: [email protected]
          Reporter: [email protected]

I am not sure this is a bug or simply expected behavior. However I see
strange results when I attempt to override the uname(3) struct members
with env vars such as UNAME_s if I use putenv 'UNAME_s=' for an empty
value.

Looking at lib/libc/stdlib/getenv.c I see : 

000644          /* Create environment entry. */
000645          envVars[envNdx].name = string;
000646          envVars[envNdx].nameLen = -1;
000647          envVars[envNdx].value = NULL;
000648          envVars[envNdx].valueSize = -1;
000649          envVars[envNdx].putenv = true;
000650          envVars[envNdx].active = true;
000651          newEnvActive++;

Which seems to be fine with the insert of an env var of zero length.
However a zero length env var named UNAME_s will destroy the system
name.

What I see in a few simple tests : 

    (1) Trivial unsetenv 

styx$ cat uname_unsetenv.c
/*
 * uname_unsetenv.c Demonstrate that FreeBSD seems to allow env var
 *                  values to override the uname(3) struct members.
 *                  We may remove the env var contents with unsetenv.
 *
 * Copyright (C) Dennis Clarke 2022
 *
 * 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 <https://www.gnu.org/licenses/>.
 *
 * https://www.gnu.org/licenses/gpl-3.0.txt
 */

/*********************************************************************
 * The Open Group Base Specifications Issue 6
 * IEEE Std 1003.1, 2004 Edition
 *
 *    An XSI-conforming application should ensure that the feature
 *    test macro _XOPEN_SOURCE is defined with the value 600 before
 *    inclusion of any header. This is needed to enable the
 *    functionality described in The _POSIX_C_SOURCE Feature Test
 *    Macro and in addition to enable the XSI extension.
 *
 *********************************************************************/
#define _XOPEN_SOURCE 600

#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/utsname.h>

int main(int argc, char *argv[])
{

    int j;
    struct utsname uname_data;
    char *env_var_value = NULL;

    /* tricky stuff about UNAME(3) :
     *
     *    These ENVIRONMENT variables override some uname struct members
     *
     *         env name     overrides
     *         --------------------------
     *         UNAME_s      sysname
     *         UNAME_r      release
     *         UNAME_v      version
     *         UNAME_m      machine
     *
     */
    char *env_var[] = {"UNAME_s","UNAME_r","UNAME_v","UNAME_m"};

    setlocale( LC_MESSAGES, "C" );

    /* check for and then unset those env vars */
    errno = 0;
    for ( j=0; j<4 ; j++ ) {
        env_var_value = getenv(env_var[j]);
        if ( env_var_value != NULL) {

            fprintf(stderr, "INFO : env var \"%s\" set to \"%s\"\n",
                    env_var[j], env_var_value);

            if (unsetenv(env_var[j]) < 0) {
                fprintf(stderr, "FAIL : could not clear env \"%s\"\n",
                        env_var[j]);

                perror("FAIL : ");
                return EXIT_FAILURE;
            } else {
                fprintf(stderr, "     : cleared env var \"%s\"\n", env_var[j]);
            }
        }
    }

    if ( uname( &uname_data ) < 0 ) {
        fprintf(stderr,
                 "WARNING : Could not attain system uname data.\n" );
        perror ("uname" );
    } else {
        printf("-------------------------------" );
        printf("------------------------------\n" );
        printf("        system name = %s\n", uname_data.sysname );
        printf("          node name = %s\n", uname_data.nodename );
        printf("            release = %s\n", uname_data.release );
        printf("            version = %s\n", uname_data.version );
        printf("            machine = %s\n", uname_data.machine );
        printf ( "-------------------------------" );
        printf ( "------------------------------" );
    }
    printf ("\n");

    return EXIT_SUCCESS;

}

Here is the system uname data :

styx$ uname -apKU 
FreeBSD styx 14.0-CURRENT FreeBSD 14.0-CURRENT #0 main-n259756-6692670f58f9:
Mon Dec 19 16:48:48 UTC 2022    
root@styx:/usr/obj/usr/src/amd64.amd64/sys/GENERIC amd64 amd64 1400075 1400075

styx$ ./uname_unsetenv
-------------------------------------------------------------
        system name = FreeBSD
          node name = styx
            release = 14.0-CURRENT
            version = FreeBSD 14.0-CURRENT #0 main-n259756-6692670f58f9: Mon
Dec 19 16:48:48 UTC 2022     root@styx:/usr/obj/usr/src/amd64.amd64/sys/GENERIC
            machine = amd64
-------------------------------------------------------------
styx$ 


We can pollute the uname struct members in FreeBSD : 

styx$ UNAME_s=system  UNAME_m=alpha64  UNAME_r=random  uname -apKU
system styx random FreeBSD 14.0-CURRENT #0 main-n259756-6692670f58f9: Mon Dec
19 16:48:48 UTC 2022     root@styx:/usr/obj/usr/src/amd64.amd64/sys/GENERIC
alpha64 amd64 1400075 1400075

The above code will remove the offending env vars : 

styx$ UNAME_s=system  UNAME_m=alpha64  UNAME_r=random ./uname_unsetenv 
INFO : env var "UNAME_s" set to "system"
     : cleared env var "UNAME_s"
INFO : env var "UNAME_r" set to "random"
     : cleared env var "UNAME_r"
INFO : env var "UNAME_m" set to "alpha64"
     : cleared env var "UNAME_m"
-------------------------------------------------------------
        system name = FreeBSD
          node name = styx
            release = 14.0-CURRENT
            version = FreeBSD 14.0-CURRENT #0 main-n259756-6692670f58f9: Mon
Dec 19 16:48:48 UTC 2022     root@styx:/usr/obj/usr/src/amd64.amd64/sys/GENERIC
            machine = amd64
-------------------------------------------------------------
styx$ 


Therefore the uname struct members are once again available to a given
process and we can determine inside an exec the system we are running
on from uname.



    (2) Try to use putenv to clear the offending env vars

styx$ cat uname_putenv.c
/*
 * uname_putenv.c   Demonstrate that FreeBSD seems to allow env var
 *                  values to override the uname(3) struct members.
 *                  We may change the env var contents with putenv
 *                  however putenv does not seem to work on the
 *                  second call.
 *
 * Copyright (C) Dennis Clarke 2022
 *
 * 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 <https://www.gnu.org/licenses/>.
 *
 * https://www.gnu.org/licenses/gpl-3.0.txt
 */

/*********************************************************************
 * The Open Group Base Specifications Issue 6
 * IEEE Std 1003.1, 2004 Edition
 *
 *    An XSI-conforming application should ensure that the feature
 *    test macro _XOPEN_SOURCE is defined with the value 600 before
 *    inclusion of any header. This is needed to enable the
 *    functionality described in The _POSIX_C_SOURCE Feature Test
 *    Macro and in addition to enable the XSI extension.
 *
 *********************************************************************/
#define _XOPEN_SOURCE 600

#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/utsname.h>

int main(int argc, char *argv[])
{

    int j;
    struct utsname uname_data;
    char *env_var_value = NULL;

    /* tricky stuff about UNAME(3) :
     *
     *    These ENVIRONMENT variables override some uname struct members
     *
     *         env name     overrides
     *         --------------------------
     *         UNAME_s      sysname
     *         UNAME_r      release
     *         UNAME_v      version
     *         UNAME_m      machine
     *
     */
    char *env_var[] = {"UNAME_s","UNAME_r","UNAME_v","UNAME_m"};

    /* These are likely not needed however the sources 
     * for putenv seem to check for the '=' character
     * as well as the degenerate case where the submitted
     * string is merely the '=' char. So these make it
     * trivial to putenv an empty string. */
    char *env_var_to_clear[] = {"UNAME_s=","UNAME_r=","UNAME_v=","UNAME_m="};

    setlocale( LC_MESSAGES, "C" );

    /* check for and then unset those env vars */
    errno = 0;
    for ( j=0; j<4 ; j++ ) {
        env_var_value = getenv(env_var[j]);
        if ( env_var_value != NULL) {

            fprintf(stderr, "INFO : env var \"%s\" set to \"%s\"\n",
                    env_var[j], env_var_value);

            if (putenv(env_var_to_clear[j]) < 0) {
                fprintf(stderr, "FAIL : could not clear env \"%s\"\n",
                        env_var[j]);

                perror("FAIL : ");
                return EXIT_FAILURE;
            } else {
                fprintf(stderr, "     : cleared env var \"%s\"\n", env_var[j]);
            }
        }
    }

    if ( uname( &uname_data ) < 0 ) {
        fprintf(stderr,
                 "WARNING : Could not attain system uname data.\n" );
        perror ("uname" );
    } else {
        printf("-------------------------------" );
        printf("------------------------------\n" );
        printf("        system name = %s\n", uname_data.sysname );
        printf("          node name = %s\n", uname_data.nodename );
        printf("            release = %s\n", uname_data.release );
        printf("            version = %s\n", uname_data.version );
        printf("            machine = %s\n", uname_data.machine );
        printf ( "-------------------------------" );
        printf ( "------------------------------" );
    }
    printf ("\n");

    return EXIT_SUCCESS;

}

styx$ 

First we have the actual system : 

styx$ uname -apKU                                                               
but why this styx 14.0-CURRENT FreeBSD 14.0-CURRENT #0
main-n259756-6692670f58f9: Mon Dec 19 16:48:48 UTC 2022    
root@styx:/usr/obj/usr/src/amd64.amd64/sys/GENERIC amd64 amd64 1400075 1400075
styx$ 

However we can wreck havok with the uname members : 

styx$ UNAME_s=system  UNAME_m=alpha64  UNAME_r=random  UNAME_v=NOTHING   uname
-apKU  
system styx random NOTHING alpha64 amd64 1400075 1400075
styx$ 

Worse, there seems to be no way to clear those values with putenv : 

styx$ 
styx$ UNAME_s=system  UNAME_m=alpha64  UNAME_r=random  UNAME_v=NOTHING  
./uname_putenv
INFO : env var "UNAME_s" set to "system"
     : cleared env var "UNAME_s"
INFO : env var "UNAME_r" set to "random"
     : cleared env var "UNAME_r"
INFO : env var "UNAME_v" set to "NOTHING"
     : cleared env var "UNAME_v"
INFO : env var "UNAME_m" set to "alpha64"
     : cleared env var "UNAME_m"
-------------------------------------------------------------
        system name = 
          node name = styx
            release = 
            version = 
            machine = 
-------------------------------------------------------------
styx$ 

So the question would be, should putenv() allow the creation of these
env vars as zero length strings?  I tested on a few other systems but
it seems only FreeBSD allows these UNAME_foo env vars to override the
uname struct members. Should putenv create zero length data for these
env vars?


Dennis Clarke
RISC-V/SPARC/PPC/ARM/CISC
UNIX and Linux spoken
GreyBeard and suspenders optional

-- 
You are receiving this mail because:
You are the assignee for the bug.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.