Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
requesting help with setenv/unsetenv
#11
Re: requesting help with setenv/unsetenv
very nice Tru, thanks for sharing, I will be studying what you coded - I actually wrote out a quick algorithm (with pencil and paper while at work) of how I was going to implement the overwrite functionality so it will be interesting for me to see how you did it vs how I was approaching it

also, about the free() part - I was under the impression its good practice to do a free after a malloc to avoid memory leaks... so the context of what your talking about is more of a security coding aspect or I would be introducing some sort of bug? I guess what I mean is I don't understand the gravity of creating a "automatic variable on stack".. thanks again for your time and feedback
gijoe77 Offline
Tezro

Posts: 354
Threads: 23
Joined: Jun 2018
Find Reply
07-20-2018, 01:46 PM
#12
Re: requesting help with setenv/unsetenv
gijoe77 post_id=1611 time=1532094391 user_id=243 Wrote:I actually wrote out a quick algorithm (with pencil and paper while at work) ...
Kudos for this one.

gijoe77 post_id=1611 time=1532094391 user_id=243 Wrote:also, about the free() part - I was under the impression its good practice to do a free after a malloc to avoid memory leaks... so the context of what your talking about is more of a security coding aspect or I would be introducing some sort of bug? I guess what I mean is I don't understand the gravity of creating a "automatic variable on stack".. thanks again for your time and feedback
I'll try to elaborate a bit on this one:

When using putenv(), what you actually do is:
1. Calculate size of the environ array of pointers
2. Create new environ array of pointers, say new_environ, with +1 entry (for the new "envname=envval" string to be added)
3. Copy pointers from environ array to new_environ array (which now includes the new string)
4. Update environ global variable with address of new_environ

The new environ array, new_environ, is created using malloc or realloc. The space for the new "envname=envval" string, is allocated using malloc too. For this second space to persist throughout lifetime of the process, the buffer/space should not be freed.

If you'd free up this buffer, this same buffer could be reused as soon as another malloc would be called, thus overwriting the "envname=envval" string. Exactly when this will happen depends on the implementation of malloc and of the buffer size you are about to allocate.

The reason your program runs fine even using free() after putenv(), is because after your free() the program does not make enough use of malloc/realloc/calloc for this same buffer to be allocated again. That's the only reason the new entry is still there in its entirety.

Ok, so what about this other "automatic variables"-thing?

When calling a function in C, for example, you allocate space for local variables:

Code:
<i>
</i>int main()
{

int a; // <- local
char *s; // <- local

static int c; // <- not local
extern char *environ; // <- not local

.
.
.
}

Nonlocal variables have their space physically allocated somewhere externally to main (in some data section of the process, accessible through global pointers, for example). OTOH, local variables have their space physically allocated on a memory region called stack, which works as follows:

When a function is called, stack space is allocated to provide space for the local variables. When this same function is exited, the *same* stack space is deallocated. The operations of allocate/deallocate are reduced to a simple increase (subtract from stack address pointer) or decrease (add to stack address pointer). You increase by subtracting because most of the time the stack increases towards lower addresses. Note this is just a convention (PA-RISC for example has their stack growing through higher addresses, so it's exactly the opposite).

An example for Tru64/Alpha:

Code:
<i>
</i>#include <stdio.h>

int main(int argc, char *argv[])
{
        printf("Hello\n");
}

Would produce the following stack allocations/deallocations:

Code:
<i>
</i># dis -p main hello
        main:
  0x120001140:  27bb2000        ldah    gp, 8192(t12)
  0x120001144:  2ffe0000        ldq_u   zero, 0(sp)
  0x120001148:  23bd6f30        lda     gp, 28464(gp)
  0x12000114c:  2ffe0000        ldq_u   zero, 0(sp)
  0x120001150:  23defff0        lda     sp, -16(sp) <-- increase stack space
...
  0x120001174:  23de0010        lda     sp, 16(sp) <-- decrease stack space
  0x120001178:  23bd6f08        lda     gp, 28424(gp)
  0x12000117c:  2ffe0000        ldq_u   zero, 0(sp)
  0x120001180:  6bfa8001        ret     zero, (ra), 1
#

When adding more local variables:

Code:
<i>
</i>#include <stdio.h>

int main(int argc, char *argv[])
{
        int i;
        int int_array[100];

        for (i = 0; i < 100; i++)
                int_array[i] = i;

        printf("Hello %i %i %i\n", int_array[0], int_array[1], int_array[2]);
}

# dis -p main hello
        main:
  0x120001100:  27bb2000        ldah    gp, 8192(t12)
  0x120001104:  2ffe0000        ldq_u   zero, 0(sp)
  0x120001108:  23bd6f70        lda     gp, 28528(gp)
  0x12000110c:  2ffe0000        ldq_u   zero, 0(sp)
  0x120001110:  23defe60        lda     sp, -416(sp) <-- 16 + int_array (100 * 4) = 416
...
  0x120001160:  23de01a0        lda     sp, 416(sp) <-- decrease it again
  0x120001164:  23bd6f1c        lda     gp, 28444(gp)
  0x120001168:  6bfa8001        ret     zero, (ra), 1
#

You may wonder why without having declared any local variable in the first example, the stack is being grown by 16. Well, there are other pieces of information saved on each function call, that require, in this case, at least 16 bytes of stack space.

And what about automatic variables, then? Local variables that get allocated on this stack space are called automatic variables because they exist after you increase stack space (function entry) and cease to be valid after you decrease the same stack space (function exit), without any intervention whatsoever of the program writer itself. It just happens *automatically* through the aformentioned machine instructions added by the compilers to each function.

putenv() + automatic variable
-------------------------------------
Since automatic variables are so short lived, if you use them for putenv() you can easily overwrite the passed string on subsequent function calls:

Code:
<i>
</i>#include <stdio.h>
#include <stdlib.h>

void A()
{
        /* A()'s local variable s */
        char s[15] = "MYNAME=MYVALUE";

        putenv(s);

        printf("A(): getenv(\"MYNAME\") = \"%s\"\n", getenv("MYNAME"));
        printf("A(): Address of s = %p\n", s);
}

void B()
{
        /* B()'s local variable is now at the same address as A()'s before !!! */
        char s[15] = "MYNAME=HIJKLMN";

        printf("B(): overwrite with s = %s\n", s);
        printf("B(): getenv(\"MYNAME\") = \"%s\"\n", getenv("MYNAME"));
        printf("B(): Address of s = %p\n", s);
}

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

        B();
}


# ./test
A(): getenv("MYNAME") = "MYVALUE"
A(): Address of s = 11fffbfd8
B(): overwrite with s = MYNAME=HIJKLMN
B(): getenv("MYNAME") = "HIJKLMN"
B(): Address of s = 11fffbfd8
#

The same applies for a putenv() call with a malloced string that is freed afterwards:

Code:
<i>
</i>#include <stdio.h>
#include <stdlib.h>


int main()
{
        int i, j;
        char *s;
        char *p;

        s = (char *) malloc(15);

        strcpy(s, "MYNAME=MYVALUE");

        putenv(s);

        /* No problem here */
        printf("getenv(\"MYNAME\") = \"%s\"\n", getenv("MYNAME"));

        free(s); /* <-- Now subsequent mallocs can reuse the same buffer! */

        /* s still exists but can be reused now */
        printf("getenv(\"MYNAME\") = \"%s\"\n", getenv("MYNAME"));

        /* Repeat often/long enough to trash previous buffer location */
        /* NOTE: Depends on malloc implementation */
        for (i = 0; i < 1000; i++)
        {
                p = (char *) malloc(15);

                /* Fill new buffers with garbage */
                for (j = 0; j < 15; j++)
                        p[j] = 'd'; /* You can put anything here */

                free(p);
        }

        /* Let's see what's left of our environment string */
        printf("getenv(\"MYNAME\") = \"%s\"\n", getenv("MYNAME"));
}

# ./test2
getenv("MYNAME") = "MYVALUE"
getenv("MYNAME") = "MYVALUE"
getenv("MYNAME") = "(null)"
#

When commenting out the free(), everything is fine:

Code:
<i>
</i># ./test2
getenv("MYNAME") = "MYVALUE"
getenv("MYNAME") = "MYVALUE"
getenv("MYNAME") = "MYVALUE"
#


Hope this helps.

Tru
TruHobbyist Offline
O2

Posts: 43
Threads: 6
Joined: May 2018
Find Reply
07-20-2018, 05:35 PM
#13
Re: requesting help with setenv/unsetenv
thanks Tru,

I will be an vacation this week with my family so I will be limited in what I can do, but I will re-read this a few times and really try to get a good handle on it. Thanks for your time on this topic
gijoe77 Offline
Tezro

Posts: 354
Threads: 23
Joined: Jun 2018
Find Reply
07-22-2018, 04:01 AM
#14
Re: requesting help with setenv/unsetenv
You're welcome.

Happy holidays
TruHobbyist Offline
O2

Posts: 43
Threads: 6
Joined: May 2018
Find Reply
07-22-2018, 09:32 AM


Forum Jump:


Users browsing this thread: 1 Guest(s)