1

From https://unix.stackexchange.com/a/436631/674

the file /proc/$$/environ ... does not reflect any changes to the environment, but just reports what the program received when it was execed by the process.

From APUE:

Each program is also passed an environment list. Like the argument list, the environment list is an array of character pointers, with each pointer containing the address of a null-terminated C string. The address of the array of pointers is contained in the global variable environ:

extern char **environ;

Access to specific environment variables is normally through the getenv and putenv functions, described in Section 7.9, instead of through the environ variable. But to go through the entire environment, the environ pointer must be used.

Are /proc/$$/environ and the global variable environ independent from each other or consistent with each other?

Do the strings accessed via environ also not reflect any changes to the environment, but just reports the environment received by execve()?

Or do the strings accessed via environ always reflect any change to them, just like that getenv always get the up-to-date environment strings?

Do the strings accessed via getenv always reflect any change and are always up-to-date?

Thanks.

Tim
  • 101,790
  • 1
    This ground has already been covered at https://unix.stackexchange.com/questions/302970/ and https://unix.stackexchange.com/questions/302948/ . – JdeBP Apr 16 '18 at 05:02

1 Answers1

2

/proc/$$/environ and the variable environ are independent. environ does reflect changes to the environment, and in fact the value of the pointer in environ also changes when environment variables are added to the environment via putenv() (but this is an implementation detail.)

We'll have to distinguish between the system call level, and the library level. At the system call level, the only mechanism related to the environment is the envp argument to the execve call. This parameter is expected to contain name=value pairs that make up the environment of the new program. This environment is copied to the stack of the new process, where the user space startup code can pick it up.

At the library level, we have

  • the global variable environ, which points to a copy of the environment
  • the functions getenv() and putenv() for examining and modifying the environment
  • the exec* family of functions (not inlcuding execve) which either implicitly (via environ) or explicitly (passed via a parameter) access the environment

The exec* library functions ultimately call the execve system call. The environ variable does not point to the environment on the stack; instead the environment is copied to the process heap before the environ variable is set up (this is again an implementation detail.)

Why doesn't /proc/$$/environ reflect changes to the environment? /proc/$$/environ is a virtual file provided by the kernel, and the kernel has no way of knowing what is going on at this low level in the address space in a user process. The kernel has no knowledge of the environ variable, and is unaware of the data structures used by the process to store the environment.

Johan Myréen
  • 13,168
  • Psst! This is no longer the only mechanism at the system call level. See https://unix.stackexchange.com/a/438007/5132 . – JdeBP Apr 16 '18 at 06:26
  • The link describes the prctl() system call with which you can, among other things, tell the kernel where in the process' memory the environment variables are stored, so /proc/$$/environ can reflect the updated environment. The process itself has to make the call. prctl() also assumes the variables are stored in the conventional way, and on the stack. But a process is free to do what it wants with the environment, including copying it to the heap, using a different data structure, etc. A shell might want to mix the environment with its internal (not yet exported) variables. – Johan Myréen Apr 16 '18 at 13:43
  • The last paragraph is kind of misleading -- the /proc/PID/environ really works by copying live stuff from the address space of the process. /proc/PID/environ will only stop reflecting the process' environment when the either the environment strings or the whole list (char **environ) has been relocated elsewhere. Just try it with a simple program like int main(){ strcpy(getenv("PATH"), "/no/where"); poll(0, 0, -1); } followed by grep -z ^PATH /proc/PID/environ. –  Aug 15 '19 at 05:11
  • @mosvy The relocation you mention happens very fast. Try changing an environment variable with setenv(), and you'll find the new string is allocated on the heap, and the change is not reflected by /proc/PID/environ. Or add a variable with setenv() and see how the variable environ now points to the heap. Yes, stuff is copied from the address space of the process, and you can poke new values to this array, but there is no guarantee that it is at all part of the data structures that libc uses to pass as the environment to a new process. – Johan Myréen Aug 15 '19 at 17:07
  • That doesn't matter. The kernel has all the ways of knowing what is going in the address space of a process -- it's just a matter of convenience how deep it goes. It's not like /proc/PID/environ is the original environment of a process (it may reflect useland changes to it). There are also ways to make it point elsewhere (via prctl(PR_SET_MM_ENV_START)), or to implement it fully in userland (via process_vm_readv() or /proc/PID/mem). And the way setenv() works in glibc is just an implementation detail. –  Aug 16 '19 at 02:47
  • @mosvy The kernel knows the address space of a process, but it knows nothing about the logic of the program. Take a look at the figure on this page. /proc/PID/environ is a copy of the memory area marked env at the top of the figure. The prctl system call can be used to change the boundaries of this area, but this is very seldom done. The libc environment handling code does not use prctl. Instead libc allocates new memory on the heap for storing new and changed variables. /proc/PID/environ still returns the same area as before, i.e. the stale values – Johan Myréen Aug 16 '19 at 16:57