1

I have an struct variable which is passed like as follows:

  //function definition
   void function1(const Node* aVAR1)
    {
        Node* value=NULL;
        .....
   }
   int main()
   {
    Node* aVAR=NULL;
    aVAR=x.value;

    function1(aVAR);
   }

Here, when I run this in gdb, and step into function1(), I see for variable aVAR one temporary memory address is created.

GDB:

21     aVAR=x.value;         
(gdb) p aVAR
$5 = (Node *) 0x654321
(gdb) n

Breakpoint 1, function1(aVAR1=0x7ffffffffebcdf ) at debug/../abc.c:12
12 {
(gdb) p aVAR1
$6 = (const Node *) 0x7ffffffffebcdf 

For example,

  1. Initially, the address of aVAR is 0x654321
  2. Later for a short while until the first instruction in function1() is not executed, aVAR1 is kept in some temporary address like 0x7ffffffffebcdf.
  3. After executing Node* value=NULL; which is first instruction in the function1(), the aVar1's address is 0x654321 again.
  4. but this temporary (0x7ffffffffebcdf) address is not cleaned up: even after the function exits, 0x7ffffffffebcdf is not cleared

I want 0x7ffffffffebcdf to be cleared after function exits but that 0x7ffffffffebcdf address does not have a pointer through which I can access this memory. Is there any option while linking in GCC through which I can prevent this?

If I add a malloc for aVAR and clear it later using memset and free, the problem gets resolved BUT logically when I see , I lose the reference to the memory block allocated by malloc() , and I won't be able to free() the allocated memory (causing memory leak ).

Ek1234
  • 407
  • 3
  • 7
  • 17
  • 4
    There are two different variables `aVar`, one is local to the caller, the other is local to the function. They have different addresses. – Barmar Jan 03 '20 at 04:22
  • but after executing "Node* value=NULL;" which is first instruction in the function1(), the aVar's address becomes 0x654321 again – Ek1234 Jan 03 '20 at 04:24
  • Can you add a transcript of your gdb session that shows what you're referring to? And show the real code of `function1()`? – Barmar Jan 03 '20 at 04:26
  • Or maybe show the disassembly of the function, where it's allocating memory that never gets freed. – Barmar Jan 03 '20 at 04:28
  • Try using a different argument name for function 1 and see if it makes more sense. – afk Jan 03 '20 at 04:30

2 Answers2

2

In what you presented, you have two variables called aVAR. The first is local var in main, and the second is function1's parameter. Both are in automatic storage (or "temporary" storage, as you call it), and thus will cease to exist when the function containing them exits. Nothing special needs to be done to free them.

Only the pointed structure needs to be freed (assuming it was malloc'ed), and that only needs to be done once, no matter how many pointers you had to it in its lifetime.

In short, all you need is one free per malloc/calloc. (Though keep in mind that strdup will call malloc, and passing NULL to realloc is effectively a malloc.)

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • So for example inside the function1() i refer aVar by aVar1, like this,void function1(const Node* aVAR1){}, which one you are suggesting to cleanup here – Ek1234 Jan 03 '20 at 04:27
  • Neither. Both pointers are in automatic storage. You only need to cleanup *pointed structure* (assuming it was `malloc`'ed). Which means you'll need `free(aVAR);` or `free(aVAR1)` or `free(x.value)` at some point if that structure was `malloc`'ed, but it doesn't matter which one you use. – ikegami Jan 03 '20 at 04:28
  • there is no life of aVAR1 temp address so i cant freeup 0x7ffffffffebcdf) and other one is already freed – Ek1234 Jan 03 '20 at 04:28
  • even after the funtion exits 0x7ffffffffebcdf is not cleared – Ek1234 Jan 03 '20 at 04:29
  • Correct. No effort is wasted clearing memory when it becomes unused. Note that accessing that memory is *Undefined Behaviour*. – ikegami Jan 03 '20 at 04:31
  • I want 0x7ffffffffebcdf to be cleared after function exits, how can i do that? – Ek1234 Jan 03 '20 at 04:32
  • `aVAR1 = NULL;` or `memset(&aVAR1, 0, sizeof(aVAR1));`. That said the compiler is probably allowed to optimize those away. – ikegami Jan 03 '20 at 04:32
  • I know it's not NULL. You asked how to clear it. You can clear it by setting it to NULL. – ikegami Jan 03 '20 at 04:34
  • I want 0x7ffffffffebcdf to be cleared after function exits but that 0x7ffffffffebcdf address does not have a pointer through which I can access this memory, is there any option while linking in gcc through which i can escape this from happening. – Ek1234 Jan 03 '20 at 04:36
  • `0x7ffffffffebcdf` is the address of `aVAR1`. `&aVAR1` would get you a pointer to it. So you can use `*&aVAR1 = NULL;` which is the same thing a `aVAR1 = NULL;`. You could also use `memset(&aVAR1, 0, sizeof(aVAR1));`. – ikegami Jan 03 '20 at 04:37
  • once I enter the function address ,just after executing the first instruction of aVAR1 becomes 0x654321. – Ek1234 Jan 03 '20 at 04:39
  • The value of `aVAR1`, yes. "The value of `aVAR1` is `0x654321`" and "The value at `0x7ffffffffebcdf` is `0x654321`" are equivalent statements. – ikegami Jan 03 '20 at 04:40
  • When I do "p aVAR1" n gdb , I get the value in aVAR1, and here when I enter the function1(), intially it is 0x7ffffffffebcdf and after first instruction it becomes 0x654321 – Ek1234 Jan 03 '20 at 04:57
  • ok, so `0x7ffffffffebcdf ` is the initial value of `aVAR1`. You said it was an address earlier on, so I assumed you meant it was the address of `aVAR1`. Your comments make no sense now. You can't clear a *value*; you can only clear a variable. To clear a variable is to set its value to zero. – ikegami Jan 03 '20 at 04:59
1

I want 0x7ffffffffebcdf to be cleared after function exits...

I have a limited imagination, but among the reasons I can imagine you want this is:

  1. You think that this is still in use; it isn’t, it is out of scope, and unreachable.
  2. If it happens to be reachable, because you have stored its address somewhere, you have made a mistake that no amount of zeroing will cure.
  3. You have a security issue, and you want to make sure temporary memory is scrubbed.

So, given [3] there are two choices; change your code to zero it before main returns; or change your main() to be mymain():

int mymain() {
    Node* aVAR=NULL;
    aVAR=x.value;

    function1(aVAR);
    return something;
}
void clearstack() {
     int data[1000];
     int fd;
     if ((fd = open("/dev/zero", O_RDONLY)) != -1) {
          read(fd, data, sizeof data);
          close(fd);
     }
}
int main() {
    int r = mymain();
    clearstack();
    return r;
}

this works because the stack addresses will overlay between the two function calls, so your 0x7f-febcdf will land in the middle of data[]. The choir of implementation defined behaviour should be warming up now. but really, you would be better off with:

int mymain() {
    Node* aVAR=NULL;
    aVAR=x.value;

    function1(aVAR);
    aVAR = 0;
    dummyfunction(&aVAR);
    return aVAR == 0;
}

Note that by providing the address of aVAR to dummyfunction, you preturb the compilers ability to remove what it might consider useless. This sort of behavior is difficult to predict, however, because it binds your program source to whatever version of whatever compiler is at your disposal; not a great prospect.

If volatile had any sort of rigor in its definition, it would be useful here, but it hasn't.

A little better would be to use malloc() to acquire the variable, then you are bound by a contract that this is memory [ whereas a local variable could be register only ], and you can scrub it before freeing it. It would be at the outer reaches of unacceptable behavior for the compiler to optimize out the scrub. It still might leave data sitting in some registers, which might leak out.

All this said; if an attacker is really out for uncovering secrets that are plaintext in your program, you might not be able to stop them. They could start your program under a debugger or hypervisor, and inspect the data at will.

There are concepts in some modern processors where the cpu can construct a sort of enclave where secrets can be safely unwrapped; but there are many flaws. ARM TrustZone's Secure/Normal world vs. OS's kernel/user mode or x86's Ring0/1/2/3? has more info.

mevets
  • 10,070
  • 1
  • 21
  • 33
  • I think you got my problem correctly, I am trying to understand the solution though, is there any defined explanation of this behavior or standard way to solve this kind of behavior. – Ek1234 Jan 06 '20 at 07:10
  • If I add a malloc for aVAR and clear it later using memset and free, the problem gets resolved but logically when I see , I lose the reference to the memory block allocated by malloc() , and I won't be able to free() the allocated memory (causing memory leak ). – Ek1234 Jan 07 '20 at 06:05
  • This is solving my problem but how allocating memory to aVAR is solving my problem I am not able to understand ...regarding memory layout, how that transient address 0x7ffffffffebcdf is cleared with malloc ? – Ek1234 Jan 07 '20 at 10:32
  • also , when i do malloc, and run same program in gdb and do "info registers" i dont find 0x7ffffffffebcdf – Ek1234 Jan 07 '20 at 11:59
  • If you *malloc()* the memory for sensitive information, you have an ability to clear it (zeroes) before freeing; and this is a contract the compiler and runtime should always abide by. Automatic variables and parameters have no such contract, so your attempts to clear them before returning, or rely on stack reallocation to clobber them, is not part of the contract. – mevets Jan 07 '20 at 14:51
  • but here references are changed , I am seeing the problem is resolved by one change.I want to understand,how?by just replacing Node* aVAR=NULL; to Node* aVAR=(Node*)malloc(sizeof(Node));, even if content in 0x7ffffffffebcdf is still exists. – Ek1234 Jan 08 '20 at 03:35