Date: Tue, 20 Dec 2022 03:23:54 +0000 From: bugzilla-noreply@freebsd.org To: standards@FreeBSD.org Subject: [Bug 268479] lib/libc/stdlib/getenv.c may have a problem with putenv() Message-ID: <bug-268479-99@https.bugs.freebsd.org/bugzilla/>
next in thread | raw e-mail | index | archive | help
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=3D268479 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: standards@FreeBSD.org Reporter: dclarke@blastwave.org 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=3D' for an empty value. Looking at lib/libc/stdlib/getenv.c I see :=20 000644 /* Create environment entry. */ 000645 envVars[envNdx].name =3D string; 000646 envVars[envNdx].nameLen =3D -1; 000647 envVars[envNdx].value =3D NULL; 000648 envVars[envNdx].valueSize =3D -1; 000649 envVars[envNdx].putenv =3D true; 000650 envVars[envNdx].active =3D 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 :=20 (1) Trivial unsetenv=20 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 =3D 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[] =3D {"UNAME_s","UNAME_r","UNAME_v","UNAME_m"}; setlocale( LC_MESSAGES, "C" ); /* check for and then unset those env vars */ errno =3D 0; for ( j=3D0; j<4 ; j++ ) { env_var_value =3D getenv(env_var[j]); if ( env_var_value !=3D 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 =3D %s\n", uname_data.sysname ); printf(" node name =3D %s\n", uname_data.nodename ); printf(" release =3D %s\n", uname_data.release ); printf(" version =3D %s\n", uname_data.version ); printf(" machine =3D %s\n", uname_data.machine ); printf ( "-------------------------------" ); printf ( "------------------------------" ); } printf ("\n"); return EXIT_SUCCESS; } Here is the system uname data : styx$ uname -apKU=20 FreeBSD styx 14.0-CURRENT FreeBSD 14.0-CURRENT #0 main-n259756-6692670f58f9: Mon Dec 19 16:48:48 UTC 2022=20=20=20=20 root@styx:/usr/obj/usr/src/amd64.amd64/sys/GENERIC amd64 amd64 1400075 1400= 075 styx$ ./uname_unsetenv ------------------------------------------------------------- system name =3D FreeBSD node name =3D styx release =3D 14.0-CURRENT version =3D 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/GEN= ERIC machine =3D amd64 ------------------------------------------------------------- styx$=20 We can pollute the uname struct members in FreeBSD :=20 styx$ UNAME_s=3Dsystem UNAME_m=3Dalpha64 UNAME_r=3Drandom uname -apKU system styx random FreeBSD 14.0-CURRENT #0 main-n259756-6692670f58f9: Mon D= ec 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 :=20 styx$ UNAME_s=3Dsystem UNAME_m=3Dalpha64 UNAME_r=3Drandom ./uname_unseten= v=20 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 =3D FreeBSD node name =3D styx release =3D 14.0-CURRENT version =3D 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/GEN= ERIC machine =3D amd64 ------------------------------------------------------------- styx$=20 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 =3D 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[] =3D {"UNAME_s","UNAME_r","UNAME_v","UNAME_m"}; /* These are likely not needed however the sources=20 * for putenv seem to check for the '=3D' character * as well as the degenerate case where the submitted * string is merely the '=3D' char. So these make it * trivial to putenv an empty string. */ char *env_var_to_clear[] =3D {"UNAME_s=3D","UNAME_r=3D","UNAME_v=3D","U= NAME_m=3D"}; setlocale( LC_MESSAGES, "C" ); /* check for and then unset those env vars */ errno =3D 0; for ( j=3D0; j<4 ; j++ ) { env_var_value =3D getenv(env_var[j]); if ( env_var_value !=3D 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 =3D %s\n", uname_data.sysname ); printf(" node name =3D %s\n", uname_data.nodename ); printf(" release =3D %s\n", uname_data.release ); printf(" version =3D %s\n", uname_data.version ); printf(" machine =3D %s\n", uname_data.machine ); printf ( "-------------------------------" ); printf ( "------------------------------" ); } printf ("\n"); return EXIT_SUCCESS; } styx$=20 First we have the actual system :=20 styx$ uname -apKU=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 but why this styx 14.0-CURRENT FreeBSD 14.0-CURRENT #0 main-n259756-6692670f58f9: Mon Dec 19 16:48:48 UTC 2022=20=20=20=20 root@styx:/usr/obj/usr/src/amd64.amd64/sys/GENERIC amd64 amd64 1400075 1400= 075 styx$=20 However we can wreck havok with the uname members :=20 styx$ UNAME_s=3Dsystem UNAME_m=3Dalpha64 UNAME_r=3Drandom UNAME_v=3DNOTH= ING uname -apKU=20=20 system styx random NOTHING alpha64 amd64 1400075 1400075 styx$=20 Worse, there seems to be no way to clear those values with putenv :=20 styx$=20 styx$ UNAME_s=3Dsystem UNAME_m=3Dalpha64 UNAME_r=3Drandom UNAME_v=3DNOTH= ING=20=20 ./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 =3D=20 node name =3D styx release =3D=20 version =3D=20 machine =3D=20 ------------------------------------------------------------- styx$=20 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 --=20 You are receiving this mail because: You are the assignee for the bug.=
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?bug-268479-99>