If the heap is initialized for security, then why is the stack uninitialized? The Next CEO of Stack OverflowRead the stack of another process?What do the different memory counters in FreeBSD mean?How can I monitor per process/per thread memory consumption (divided into heap, stack, data, code)?Which parts of Memory can Swap SupportWhy does 8250 UART driver not wake up TTY if more than 256 characters are pending?Trying to understanding startup procedure of monitWhy must the stack VMA be executable?When is the heap used for dynamic memory allocation?Is “writeback throttling” a solution to the “USB-stick stall problem”?What happens to the old stack, heap, and (initialized and uninitialized) data segments after execve() call?
What is the point of a new vote on May's deal when the indicative votes suggest she will not win?
What is the difference between "behavior" and "behaviour"?
How to draw fully connected graph link picture bellow in latex?
Visit to the USA with ESTA approved before trip to Iran
How to Reset Passwords on Multiple Websites Easily?
Describing a person. What needs to be mentioned?
Why is Miller's case titled R (Miller)?
How do we know the LHC results are robust?
How can I open an app using Terminal?
What is the meaning of "rider"?
If the heap is initialized for security, then why is the stack uninitialized?
How to be diplomatic in refusing to write code that breaches the privacy of our users
How long to clear the 'suck zone' of a turbofan after start is initiated?
Implement the Thanos sorting algorithm
Trouble understanding the speech of overseas colleagues
Grabbing quick drinks
How do I get the green key off the shelf in the Dobby level of Lego Harry Potter 2?
MAZDA 3 2006 (UK) - poor acceleration then takes off at 3250 revs
I believe this to be a fraud
Return the Closest Prime Number
Inappropriate reference requests from Journal reviewers
Why does GHC infer a monomorphic type here, even with MonomorphismRestriction disabled?
Why did we only see the N-1 starfighters in one film?
How do scammers retract money, while you can’t?
If the heap is initialized for security, then why is the stack uninitialized?
The Next CEO of Stack OverflowRead the stack of another process?What do the different memory counters in FreeBSD mean?How can I monitor per process/per thread memory consumption (divided into heap, stack, data, code)?Which parts of Memory can Swap SupportWhy does 8250 UART driver not wake up TTY if more than 256 characters are pending?Trying to understanding startup procedure of monitWhy must the stack VMA be executable?When is the heap used for dynamic memory allocation?Is “writeback throttling” a solution to the “USB-stick stall problem”?What happens to the old stack, heap, and (initialized and uninitialized) data segments after execve() call?
On my Debian GNU/Linux 9 system, when a binary is executed,
- the stack is uninitialized but
- the heap is zero-initialized.
Why?
I assume that zero-initialization promotes security but, if for the heap, then why not also for the stack? Does the stack, too, not need security?
My question is not specific to Debian as far as I know.
Sample C code:
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
const size_t n = 8;
// --------------------------------------------------------------------
// UNINTERESTING CODE
// --------------------------------------------------------------------
static void print_array(
const int *const p, const size_t size, const char *const name
)
printf("%s at %p: ", name, p);
for (size_t i = 0; i < size; ++i) printf("%d ", p[i]);
printf("n");
// --------------------------------------------------------------------
// INTERESTING CODE
// --------------------------------------------------------------------
int main()
int a[n];
int *const b = malloc(n*sizeof(int));
print_array(a, n, "a");
print_array(b, n, "b");
free(b);
return 0;
Output:
a at 0x7ffe118997e0: 194 0 294230047 32766 294230046 32766 -550453275 32713
b at 0x561d4bbfe010: 0 0 0 0 0 0 0 0
The C standard does not ask malloc()
to clear memory before allocating it, of course, but my C program is merely for illustration. The question is not a question about C or about C's standard library. Rather, the question is a question about why the kernel and/or run-time loader are zeroing the heap but not the stack.
ANOTHER EXPERIMENT
My question regards observable GNU/Linux behavior rather than the requirements of standards documents. If unsure what I mean, then try this code, which invokes further undefined behavior (undefined, that is, as far as the C standard is concerned) to illustrate the point:
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
const size_t n = 4;
int main()
for (size_t i = n; i; --i)
int *const p = malloc(sizeof(int));
printf("%p %d ", p, *p);
++*p;
printf("%dn", *p);
free(p);
return 0;
Output from my machine:
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
As far as the C standard is concerned, behavior is undefined, so my question does not regard the C standard. A call to malloc()
need not return the same address each time but, since this call to malloc()
does indeed happen to return the same address each time, it is interesting to notice that the memory, which is on the heap, is zeroed each time.
The stack, by contrast, had not seemed to be zeroed.
I do not know what the latter code will do on your machine, since I do not know which layer of the GNU/Linux system is causing the observed behavior. You can but try it.
linux security memory
add a comment |
On my Debian GNU/Linux 9 system, when a binary is executed,
- the stack is uninitialized but
- the heap is zero-initialized.
Why?
I assume that zero-initialization promotes security but, if for the heap, then why not also for the stack? Does the stack, too, not need security?
My question is not specific to Debian as far as I know.
Sample C code:
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
const size_t n = 8;
// --------------------------------------------------------------------
// UNINTERESTING CODE
// --------------------------------------------------------------------
static void print_array(
const int *const p, const size_t size, const char *const name
)
printf("%s at %p: ", name, p);
for (size_t i = 0; i < size; ++i) printf("%d ", p[i]);
printf("n");
// --------------------------------------------------------------------
// INTERESTING CODE
// --------------------------------------------------------------------
int main()
int a[n];
int *const b = malloc(n*sizeof(int));
print_array(a, n, "a");
print_array(b, n, "b");
free(b);
return 0;
Output:
a at 0x7ffe118997e0: 194 0 294230047 32766 294230046 32766 -550453275 32713
b at 0x561d4bbfe010: 0 0 0 0 0 0 0 0
The C standard does not ask malloc()
to clear memory before allocating it, of course, but my C program is merely for illustration. The question is not a question about C or about C's standard library. Rather, the question is a question about why the kernel and/or run-time loader are zeroing the heap but not the stack.
ANOTHER EXPERIMENT
My question regards observable GNU/Linux behavior rather than the requirements of standards documents. If unsure what I mean, then try this code, which invokes further undefined behavior (undefined, that is, as far as the C standard is concerned) to illustrate the point:
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
const size_t n = 4;
int main()
for (size_t i = n; i; --i)
int *const p = malloc(sizeof(int));
printf("%p %d ", p, *p);
++*p;
printf("%dn", *p);
free(p);
return 0;
Output from my machine:
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
As far as the C standard is concerned, behavior is undefined, so my question does not regard the C standard. A call to malloc()
need not return the same address each time but, since this call to malloc()
does indeed happen to return the same address each time, it is interesting to notice that the memory, which is on the heap, is zeroed each time.
The stack, by contrast, had not seemed to be zeroed.
I do not know what the latter code will do on your machine, since I do not know which layer of the GNU/Linux system is causing the observed behavior. You can but try it.
linux security memory
For what it's worth, your most recent code returns different addresses and (occasional) uninitialised (non-zero) data when run on OpenBSD. This obviously does not say anything about the behaviour that you are witnessing on Linux.
– Kusalananda♦
15 mins ago
Please do not change the scope of your question, and do not try to edit it in order to make answers and comments redundant. In C, the "heap" is nothing else but the memory returned by malloc() and calloc(), and only the latter is zeroing out the memory; thenew
operator in C++ (also "heap") is on Linux just a wrapper for malloc(); the kernel doesn't know nor care what the "heap" is.
– mosvy
7 mins ago
Your second example is simply exposing an artifact of the malloc implementation in glibc; if you do that repeated malloc/free with a buffer larger than 8 bytes, you will clearly see that only the first 8 bytes are zeroed.
– mosvy
7 mins ago
add a comment |
On my Debian GNU/Linux 9 system, when a binary is executed,
- the stack is uninitialized but
- the heap is zero-initialized.
Why?
I assume that zero-initialization promotes security but, if for the heap, then why not also for the stack? Does the stack, too, not need security?
My question is not specific to Debian as far as I know.
Sample C code:
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
const size_t n = 8;
// --------------------------------------------------------------------
// UNINTERESTING CODE
// --------------------------------------------------------------------
static void print_array(
const int *const p, const size_t size, const char *const name
)
printf("%s at %p: ", name, p);
for (size_t i = 0; i < size; ++i) printf("%d ", p[i]);
printf("n");
// --------------------------------------------------------------------
// INTERESTING CODE
// --------------------------------------------------------------------
int main()
int a[n];
int *const b = malloc(n*sizeof(int));
print_array(a, n, "a");
print_array(b, n, "b");
free(b);
return 0;
Output:
a at 0x7ffe118997e0: 194 0 294230047 32766 294230046 32766 -550453275 32713
b at 0x561d4bbfe010: 0 0 0 0 0 0 0 0
The C standard does not ask malloc()
to clear memory before allocating it, of course, but my C program is merely for illustration. The question is not a question about C or about C's standard library. Rather, the question is a question about why the kernel and/or run-time loader are zeroing the heap but not the stack.
ANOTHER EXPERIMENT
My question regards observable GNU/Linux behavior rather than the requirements of standards documents. If unsure what I mean, then try this code, which invokes further undefined behavior (undefined, that is, as far as the C standard is concerned) to illustrate the point:
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
const size_t n = 4;
int main()
for (size_t i = n; i; --i)
int *const p = malloc(sizeof(int));
printf("%p %d ", p, *p);
++*p;
printf("%dn", *p);
free(p);
return 0;
Output from my machine:
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
As far as the C standard is concerned, behavior is undefined, so my question does not regard the C standard. A call to malloc()
need not return the same address each time but, since this call to malloc()
does indeed happen to return the same address each time, it is interesting to notice that the memory, which is on the heap, is zeroed each time.
The stack, by contrast, had not seemed to be zeroed.
I do not know what the latter code will do on your machine, since I do not know which layer of the GNU/Linux system is causing the observed behavior. You can but try it.
linux security memory
On my Debian GNU/Linux 9 system, when a binary is executed,
- the stack is uninitialized but
- the heap is zero-initialized.
Why?
I assume that zero-initialization promotes security but, if for the heap, then why not also for the stack? Does the stack, too, not need security?
My question is not specific to Debian as far as I know.
Sample C code:
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
const size_t n = 8;
// --------------------------------------------------------------------
// UNINTERESTING CODE
// --------------------------------------------------------------------
static void print_array(
const int *const p, const size_t size, const char *const name
)
printf("%s at %p: ", name, p);
for (size_t i = 0; i < size; ++i) printf("%d ", p[i]);
printf("n");
// --------------------------------------------------------------------
// INTERESTING CODE
// --------------------------------------------------------------------
int main()
int a[n];
int *const b = malloc(n*sizeof(int));
print_array(a, n, "a");
print_array(b, n, "b");
free(b);
return 0;
Output:
a at 0x7ffe118997e0: 194 0 294230047 32766 294230046 32766 -550453275 32713
b at 0x561d4bbfe010: 0 0 0 0 0 0 0 0
The C standard does not ask malloc()
to clear memory before allocating it, of course, but my C program is merely for illustration. The question is not a question about C or about C's standard library. Rather, the question is a question about why the kernel and/or run-time loader are zeroing the heap but not the stack.
ANOTHER EXPERIMENT
My question regards observable GNU/Linux behavior rather than the requirements of standards documents. If unsure what I mean, then try this code, which invokes further undefined behavior (undefined, that is, as far as the C standard is concerned) to illustrate the point:
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
const size_t n = 4;
int main()
for (size_t i = n; i; --i)
int *const p = malloc(sizeof(int));
printf("%p %d ", p, *p);
++*p;
printf("%dn", *p);
free(p);
return 0;
Output from my machine:
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
As far as the C standard is concerned, behavior is undefined, so my question does not regard the C standard. A call to malloc()
need not return the same address each time but, since this call to malloc()
does indeed happen to return the same address each time, it is interesting to notice that the memory, which is on the heap, is zeroed each time.
The stack, by contrast, had not seemed to be zeroed.
I do not know what the latter code will do on your machine, since I do not know which layer of the GNU/Linux system is causing the observed behavior. You can but try it.
linux security memory
linux security memory
edited 37 mins ago
thb
asked 6 hours ago
thbthb
534415
534415
For what it's worth, your most recent code returns different addresses and (occasional) uninitialised (non-zero) data when run on OpenBSD. This obviously does not say anything about the behaviour that you are witnessing on Linux.
– Kusalananda♦
15 mins ago
Please do not change the scope of your question, and do not try to edit it in order to make answers and comments redundant. In C, the "heap" is nothing else but the memory returned by malloc() and calloc(), and only the latter is zeroing out the memory; thenew
operator in C++ (also "heap") is on Linux just a wrapper for malloc(); the kernel doesn't know nor care what the "heap" is.
– mosvy
7 mins ago
Your second example is simply exposing an artifact of the malloc implementation in glibc; if you do that repeated malloc/free with a buffer larger than 8 bytes, you will clearly see that only the first 8 bytes are zeroed.
– mosvy
7 mins ago
add a comment |
For what it's worth, your most recent code returns different addresses and (occasional) uninitialised (non-zero) data when run on OpenBSD. This obviously does not say anything about the behaviour that you are witnessing on Linux.
– Kusalananda♦
15 mins ago
Please do not change the scope of your question, and do not try to edit it in order to make answers and comments redundant. In C, the "heap" is nothing else but the memory returned by malloc() and calloc(), and only the latter is zeroing out the memory; thenew
operator in C++ (also "heap") is on Linux just a wrapper for malloc(); the kernel doesn't know nor care what the "heap" is.
– mosvy
7 mins ago
Your second example is simply exposing an artifact of the malloc implementation in glibc; if you do that repeated malloc/free with a buffer larger than 8 bytes, you will clearly see that only the first 8 bytes are zeroed.
– mosvy
7 mins ago
For what it's worth, your most recent code returns different addresses and (occasional) uninitialised (non-zero) data when run on OpenBSD. This obviously does not say anything about the behaviour that you are witnessing on Linux.
– Kusalananda♦
15 mins ago
For what it's worth, your most recent code returns different addresses and (occasional) uninitialised (non-zero) data when run on OpenBSD. This obviously does not say anything about the behaviour that you are witnessing on Linux.
– Kusalananda♦
15 mins ago
Please do not change the scope of your question, and do not try to edit it in order to make answers and comments redundant. In C, the "heap" is nothing else but the memory returned by malloc() and calloc(), and only the latter is zeroing out the memory; the
new
operator in C++ (also "heap") is on Linux just a wrapper for malloc(); the kernel doesn't know nor care what the "heap" is.– mosvy
7 mins ago
Please do not change the scope of your question, and do not try to edit it in order to make answers and comments redundant. In C, the "heap" is nothing else but the memory returned by malloc() and calloc(), and only the latter is zeroing out the memory; the
new
operator in C++ (also "heap") is on Linux just a wrapper for malloc(); the kernel doesn't know nor care what the "heap" is.– mosvy
7 mins ago
Your second example is simply exposing an artifact of the malloc implementation in glibc; if you do that repeated malloc/free with a buffer larger than 8 bytes, you will clearly see that only the first 8 bytes are zeroed.
– mosvy
7 mins ago
Your second example is simply exposing an artifact of the malloc implementation in glibc; if you do that repeated malloc/free with a buffer larger than 8 bytes, you will clearly see that only the first 8 bytes are zeroed.
– mosvy
7 mins ago
add a comment |
3 Answers
3
active
oldest
votes
Regardless of how the stack is initialised, you’re not seeing a pristine stack, because the C library does a number of things before calling main
, and they touch the stack.
With the GNU C library, on x86-64, execution starts at the _start entry point, which calls __libc_start_main
to set things up, and the latter ends up calling main
. But before calling main
, it calls a number of other functions, which causes various pieces of data to be written to the stack. The stack’s contents aren’t cleared in between function calls, so when you get into main
, your stack contains leftovers from the previous function calls.
This only explains the results you get from the stack, see the other answers regarding your general approach and assumptions.
Note that by the timemain()
is called, initialization routines may very well have modified memory returned bymalloc()
- especially if C++ libraries are linked in. Assuming the "heap" is initialized to anything is a really, really bad assumption.
– Andrew Henle
57 mins ago
add a comment |
The storage returned by malloc() is not zero-initialized. Do not ever assume it is.
In your test program, it's just a fluke: I guess the malloc()
just got a fresh block off mmap()
, but don't rely on that, either.
For an example, if I run your program on my machine this way:
$ echo 'void __attribute__((constructor)) p(void)
void *b = malloc(4444); memset(b, 4, 4444); free(b);
' | cc -include stdlib.h -include string.h -xc - -shared -o pollute.so
$ LD_PRELOAD=./pollute.so ./your_program
a at 0x7ffd40d3aa60: 1256994848 21891 1256994464 21891 1087613792 32765 0 0
b at 0x55834c75d010: 67372036 67372036 67372036 67372036 67372036 67372036 67372036 67372036
Well, yes, but this is why I have asked the question here rather than on Stack Overflow. My question was not about the C standard but about the way modern GNU/Linux systems typically link and load binaries. Your LD_PRELOAD is humorous but answers another question than the question I had meant to ask.
– thb
1 hour ago
1
I'm happy I made you laugh, but your assumptions and prejudices aren't funny at all. On a "modern GNU/Linux system", binaries are typically loaded by a dynamic linker, which is running constructors from dynamic libraries before getting to the main() function from your program. On your very Debian GNU/Linux 9 system, both malloc() and free() will be called more than once before the main() function from your program, even when not using any preloaded libraries.
– mosvy
1 hour ago
I have added to the question a second experiment to show that, regardless of the minimum requirements of the C standard, the memory is in fact observably initialized.
– thb
27 mins ago
add a comment |
In both cases, you get uninitialized memory, and you can't make any assumptions about its contents.
When the OS has to apportion a new page to your process, it guarantees that it won't expose data from other processes; the usual way to ensure that is to fill it with zeros (but it's equally valid to overwrite with anything else, including even a page worth of /dev/urandom
- in fact some debugging malloc()
implementations write non-zero patterns, to catch mistaken assumptions such as yours).
If malloc()
can satisfy the request from memory already used in this process, its contents won't be cleared (in fact, the clearing is nothing to do with malloc()
and it can't be - it has to happen before the memory is mapped into your address space). You may get memory that has previously been written by your process/program (e.g. before main()
).
Your answer is good as far as it goes, +1, but my question has lent itself to misunderstanding. I have edited the question to clarify that I am not asking not about the requirements of the C standard (if I were asking that, I would ask on Stack Overflow) but about the observed behavior of GNU/Linux systems.
– thb
1 hour ago
A week or two ago, I tried a different experiment, callingmalloc()
andfree()
repeatedly. Though nothing requiresmalloc()
to reuse the same storage recently freed, in the experiment,malloc()
did happen to do that. It happened to return the same address each time, but also nulled the memory each time, which I had not expected. This was interesting to me. Further experiments have led to today's question.
– thb
1 hour ago
Uhh, if you call a low enough level call it is indeed guaranteed to be filled with zeros.
– Joshua
43 mins ago
If it interests you, I have added the second experiment to the question.
– thb
33 mins ago
Perhaps I'm not being clear enough - most implementations ofmalloc()
do absolutely nothing with the memory they hand you - it's either previously-used, or freshly-assigned (and therefore zeroed by the OS). In your test, you evidently got the latter. Similarly, the stack memory is given to your process in the cleared state, but you don't examine it far enough to see parts your process hasn't yet touched.
– Toby Speight
16 secs ago
add a comment |
Your Answer
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "106"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f509232%2fif-the-heap-is-initialized-for-security-then-why-is-the-stack-uninitialized%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
Regardless of how the stack is initialised, you’re not seeing a pristine stack, because the C library does a number of things before calling main
, and they touch the stack.
With the GNU C library, on x86-64, execution starts at the _start entry point, which calls __libc_start_main
to set things up, and the latter ends up calling main
. But before calling main
, it calls a number of other functions, which causes various pieces of data to be written to the stack. The stack’s contents aren’t cleared in between function calls, so when you get into main
, your stack contains leftovers from the previous function calls.
This only explains the results you get from the stack, see the other answers regarding your general approach and assumptions.
Note that by the timemain()
is called, initialization routines may very well have modified memory returned bymalloc()
- especially if C++ libraries are linked in. Assuming the "heap" is initialized to anything is a really, really bad assumption.
– Andrew Henle
57 mins ago
add a comment |
Regardless of how the stack is initialised, you’re not seeing a pristine stack, because the C library does a number of things before calling main
, and they touch the stack.
With the GNU C library, on x86-64, execution starts at the _start entry point, which calls __libc_start_main
to set things up, and the latter ends up calling main
. But before calling main
, it calls a number of other functions, which causes various pieces of data to be written to the stack. The stack’s contents aren’t cleared in between function calls, so when you get into main
, your stack contains leftovers from the previous function calls.
This only explains the results you get from the stack, see the other answers regarding your general approach and assumptions.
Note that by the timemain()
is called, initialization routines may very well have modified memory returned bymalloc()
- especially if C++ libraries are linked in. Assuming the "heap" is initialized to anything is a really, really bad assumption.
– Andrew Henle
57 mins ago
add a comment |
Regardless of how the stack is initialised, you’re not seeing a pristine stack, because the C library does a number of things before calling main
, and they touch the stack.
With the GNU C library, on x86-64, execution starts at the _start entry point, which calls __libc_start_main
to set things up, and the latter ends up calling main
. But before calling main
, it calls a number of other functions, which causes various pieces of data to be written to the stack. The stack’s contents aren’t cleared in between function calls, so when you get into main
, your stack contains leftovers from the previous function calls.
This only explains the results you get from the stack, see the other answers regarding your general approach and assumptions.
Regardless of how the stack is initialised, you’re not seeing a pristine stack, because the C library does a number of things before calling main
, and they touch the stack.
With the GNU C library, on x86-64, execution starts at the _start entry point, which calls __libc_start_main
to set things up, and the latter ends up calling main
. But before calling main
, it calls a number of other functions, which causes various pieces of data to be written to the stack. The stack’s contents aren’t cleared in between function calls, so when you get into main
, your stack contains leftovers from the previous function calls.
This only explains the results you get from the stack, see the other answers regarding your general approach and assumptions.
edited 44 mins ago
answered 4 hours ago
Stephen KittStephen Kitt
178k24406483
178k24406483
Note that by the timemain()
is called, initialization routines may very well have modified memory returned bymalloc()
- especially if C++ libraries are linked in. Assuming the "heap" is initialized to anything is a really, really bad assumption.
– Andrew Henle
57 mins ago
add a comment |
Note that by the timemain()
is called, initialization routines may very well have modified memory returned bymalloc()
- especially if C++ libraries are linked in. Assuming the "heap" is initialized to anything is a really, really bad assumption.
– Andrew Henle
57 mins ago
Note that by the time
main()
is called, initialization routines may very well have modified memory returned by malloc()
- especially if C++ libraries are linked in. Assuming the "heap" is initialized to anything is a really, really bad assumption.– Andrew Henle
57 mins ago
Note that by the time
main()
is called, initialization routines may very well have modified memory returned by malloc()
- especially if C++ libraries are linked in. Assuming the "heap" is initialized to anything is a really, really bad assumption.– Andrew Henle
57 mins ago
add a comment |
The storage returned by malloc() is not zero-initialized. Do not ever assume it is.
In your test program, it's just a fluke: I guess the malloc()
just got a fresh block off mmap()
, but don't rely on that, either.
For an example, if I run your program on my machine this way:
$ echo 'void __attribute__((constructor)) p(void)
void *b = malloc(4444); memset(b, 4, 4444); free(b);
' | cc -include stdlib.h -include string.h -xc - -shared -o pollute.so
$ LD_PRELOAD=./pollute.so ./your_program
a at 0x7ffd40d3aa60: 1256994848 21891 1256994464 21891 1087613792 32765 0 0
b at 0x55834c75d010: 67372036 67372036 67372036 67372036 67372036 67372036 67372036 67372036
Well, yes, but this is why I have asked the question here rather than on Stack Overflow. My question was not about the C standard but about the way modern GNU/Linux systems typically link and load binaries. Your LD_PRELOAD is humorous but answers another question than the question I had meant to ask.
– thb
1 hour ago
1
I'm happy I made you laugh, but your assumptions and prejudices aren't funny at all. On a "modern GNU/Linux system", binaries are typically loaded by a dynamic linker, which is running constructors from dynamic libraries before getting to the main() function from your program. On your very Debian GNU/Linux 9 system, both malloc() and free() will be called more than once before the main() function from your program, even when not using any preloaded libraries.
– mosvy
1 hour ago
I have added to the question a second experiment to show that, regardless of the minimum requirements of the C standard, the memory is in fact observably initialized.
– thb
27 mins ago
add a comment |
The storage returned by malloc() is not zero-initialized. Do not ever assume it is.
In your test program, it's just a fluke: I guess the malloc()
just got a fresh block off mmap()
, but don't rely on that, either.
For an example, if I run your program on my machine this way:
$ echo 'void __attribute__((constructor)) p(void)
void *b = malloc(4444); memset(b, 4, 4444); free(b);
' | cc -include stdlib.h -include string.h -xc - -shared -o pollute.so
$ LD_PRELOAD=./pollute.so ./your_program
a at 0x7ffd40d3aa60: 1256994848 21891 1256994464 21891 1087613792 32765 0 0
b at 0x55834c75d010: 67372036 67372036 67372036 67372036 67372036 67372036 67372036 67372036
Well, yes, but this is why I have asked the question here rather than on Stack Overflow. My question was not about the C standard but about the way modern GNU/Linux systems typically link and load binaries. Your LD_PRELOAD is humorous but answers another question than the question I had meant to ask.
– thb
1 hour ago
1
I'm happy I made you laugh, but your assumptions and prejudices aren't funny at all. On a "modern GNU/Linux system", binaries are typically loaded by a dynamic linker, which is running constructors from dynamic libraries before getting to the main() function from your program. On your very Debian GNU/Linux 9 system, both malloc() and free() will be called more than once before the main() function from your program, even when not using any preloaded libraries.
– mosvy
1 hour ago
I have added to the question a second experiment to show that, regardless of the minimum requirements of the C standard, the memory is in fact observably initialized.
– thb
27 mins ago
add a comment |
The storage returned by malloc() is not zero-initialized. Do not ever assume it is.
In your test program, it's just a fluke: I guess the malloc()
just got a fresh block off mmap()
, but don't rely on that, either.
For an example, if I run your program on my machine this way:
$ echo 'void __attribute__((constructor)) p(void)
void *b = malloc(4444); memset(b, 4, 4444); free(b);
' | cc -include stdlib.h -include string.h -xc - -shared -o pollute.so
$ LD_PRELOAD=./pollute.so ./your_program
a at 0x7ffd40d3aa60: 1256994848 21891 1256994464 21891 1087613792 32765 0 0
b at 0x55834c75d010: 67372036 67372036 67372036 67372036 67372036 67372036 67372036 67372036
The storage returned by malloc() is not zero-initialized. Do not ever assume it is.
In your test program, it's just a fluke: I guess the malloc()
just got a fresh block off mmap()
, but don't rely on that, either.
For an example, if I run your program on my machine this way:
$ echo 'void __attribute__((constructor)) p(void)
void *b = malloc(4444); memset(b, 4, 4444); free(b);
' | cc -include stdlib.h -include string.h -xc - -shared -o pollute.so
$ LD_PRELOAD=./pollute.so ./your_program
a at 0x7ffd40d3aa60: 1256994848 21891 1256994464 21891 1087613792 32765 0 0
b at 0x55834c75d010: 67372036 67372036 67372036 67372036 67372036 67372036 67372036 67372036
answered 2 hours ago
mosvymosvy
8,6171732
8,6171732
Well, yes, but this is why I have asked the question here rather than on Stack Overflow. My question was not about the C standard but about the way modern GNU/Linux systems typically link and load binaries. Your LD_PRELOAD is humorous but answers another question than the question I had meant to ask.
– thb
1 hour ago
1
I'm happy I made you laugh, but your assumptions and prejudices aren't funny at all. On a "modern GNU/Linux system", binaries are typically loaded by a dynamic linker, which is running constructors from dynamic libraries before getting to the main() function from your program. On your very Debian GNU/Linux 9 system, both malloc() and free() will be called more than once before the main() function from your program, even when not using any preloaded libraries.
– mosvy
1 hour ago
I have added to the question a second experiment to show that, regardless of the minimum requirements of the C standard, the memory is in fact observably initialized.
– thb
27 mins ago
add a comment |
Well, yes, but this is why I have asked the question here rather than on Stack Overflow. My question was not about the C standard but about the way modern GNU/Linux systems typically link and load binaries. Your LD_PRELOAD is humorous but answers another question than the question I had meant to ask.
– thb
1 hour ago
1
I'm happy I made you laugh, but your assumptions and prejudices aren't funny at all. On a "modern GNU/Linux system", binaries are typically loaded by a dynamic linker, which is running constructors from dynamic libraries before getting to the main() function from your program. On your very Debian GNU/Linux 9 system, both malloc() and free() will be called more than once before the main() function from your program, even when not using any preloaded libraries.
– mosvy
1 hour ago
I have added to the question a second experiment to show that, regardless of the minimum requirements of the C standard, the memory is in fact observably initialized.
– thb
27 mins ago
Well, yes, but this is why I have asked the question here rather than on Stack Overflow. My question was not about the C standard but about the way modern GNU/Linux systems typically link and load binaries. Your LD_PRELOAD is humorous but answers another question than the question I had meant to ask.
– thb
1 hour ago
Well, yes, but this is why I have asked the question here rather than on Stack Overflow. My question was not about the C standard but about the way modern GNU/Linux systems typically link and load binaries. Your LD_PRELOAD is humorous but answers another question than the question I had meant to ask.
– thb
1 hour ago
1
1
I'm happy I made you laugh, but your assumptions and prejudices aren't funny at all. On a "modern GNU/Linux system", binaries are typically loaded by a dynamic linker, which is running constructors from dynamic libraries before getting to the main() function from your program. On your very Debian GNU/Linux 9 system, both malloc() and free() will be called more than once before the main() function from your program, even when not using any preloaded libraries.
– mosvy
1 hour ago
I'm happy I made you laugh, but your assumptions and prejudices aren't funny at all. On a "modern GNU/Linux system", binaries are typically loaded by a dynamic linker, which is running constructors from dynamic libraries before getting to the main() function from your program. On your very Debian GNU/Linux 9 system, both malloc() and free() will be called more than once before the main() function from your program, even when not using any preloaded libraries.
– mosvy
1 hour ago
I have added to the question a second experiment to show that, regardless of the minimum requirements of the C standard, the memory is in fact observably initialized.
– thb
27 mins ago
I have added to the question a second experiment to show that, regardless of the minimum requirements of the C standard, the memory is in fact observably initialized.
– thb
27 mins ago
add a comment |
In both cases, you get uninitialized memory, and you can't make any assumptions about its contents.
When the OS has to apportion a new page to your process, it guarantees that it won't expose data from other processes; the usual way to ensure that is to fill it with zeros (but it's equally valid to overwrite with anything else, including even a page worth of /dev/urandom
- in fact some debugging malloc()
implementations write non-zero patterns, to catch mistaken assumptions such as yours).
If malloc()
can satisfy the request from memory already used in this process, its contents won't be cleared (in fact, the clearing is nothing to do with malloc()
and it can't be - it has to happen before the memory is mapped into your address space). You may get memory that has previously been written by your process/program (e.g. before main()
).
Your answer is good as far as it goes, +1, but my question has lent itself to misunderstanding. I have edited the question to clarify that I am not asking not about the requirements of the C standard (if I were asking that, I would ask on Stack Overflow) but about the observed behavior of GNU/Linux systems.
– thb
1 hour ago
A week or two ago, I tried a different experiment, callingmalloc()
andfree()
repeatedly. Though nothing requiresmalloc()
to reuse the same storage recently freed, in the experiment,malloc()
did happen to do that. It happened to return the same address each time, but also nulled the memory each time, which I had not expected. This was interesting to me. Further experiments have led to today's question.
– thb
1 hour ago
Uhh, if you call a low enough level call it is indeed guaranteed to be filled with zeros.
– Joshua
43 mins ago
If it interests you, I have added the second experiment to the question.
– thb
33 mins ago
Perhaps I'm not being clear enough - most implementations ofmalloc()
do absolutely nothing with the memory they hand you - it's either previously-used, or freshly-assigned (and therefore zeroed by the OS). In your test, you evidently got the latter. Similarly, the stack memory is given to your process in the cleared state, but you don't examine it far enough to see parts your process hasn't yet touched.
– Toby Speight
16 secs ago
add a comment |
In both cases, you get uninitialized memory, and you can't make any assumptions about its contents.
When the OS has to apportion a new page to your process, it guarantees that it won't expose data from other processes; the usual way to ensure that is to fill it with zeros (but it's equally valid to overwrite with anything else, including even a page worth of /dev/urandom
- in fact some debugging malloc()
implementations write non-zero patterns, to catch mistaken assumptions such as yours).
If malloc()
can satisfy the request from memory already used in this process, its contents won't be cleared (in fact, the clearing is nothing to do with malloc()
and it can't be - it has to happen before the memory is mapped into your address space). You may get memory that has previously been written by your process/program (e.g. before main()
).
Your answer is good as far as it goes, +1, but my question has lent itself to misunderstanding. I have edited the question to clarify that I am not asking not about the requirements of the C standard (if I were asking that, I would ask on Stack Overflow) but about the observed behavior of GNU/Linux systems.
– thb
1 hour ago
A week or two ago, I tried a different experiment, callingmalloc()
andfree()
repeatedly. Though nothing requiresmalloc()
to reuse the same storage recently freed, in the experiment,malloc()
did happen to do that. It happened to return the same address each time, but also nulled the memory each time, which I had not expected. This was interesting to me. Further experiments have led to today's question.
– thb
1 hour ago
Uhh, if you call a low enough level call it is indeed guaranteed to be filled with zeros.
– Joshua
43 mins ago
If it interests you, I have added the second experiment to the question.
– thb
33 mins ago
Perhaps I'm not being clear enough - most implementations ofmalloc()
do absolutely nothing with the memory they hand you - it's either previously-used, or freshly-assigned (and therefore zeroed by the OS). In your test, you evidently got the latter. Similarly, the stack memory is given to your process in the cleared state, but you don't examine it far enough to see parts your process hasn't yet touched.
– Toby Speight
16 secs ago
add a comment |
In both cases, you get uninitialized memory, and you can't make any assumptions about its contents.
When the OS has to apportion a new page to your process, it guarantees that it won't expose data from other processes; the usual way to ensure that is to fill it with zeros (but it's equally valid to overwrite with anything else, including even a page worth of /dev/urandom
- in fact some debugging malloc()
implementations write non-zero patterns, to catch mistaken assumptions such as yours).
If malloc()
can satisfy the request from memory already used in this process, its contents won't be cleared (in fact, the clearing is nothing to do with malloc()
and it can't be - it has to happen before the memory is mapped into your address space). You may get memory that has previously been written by your process/program (e.g. before main()
).
In both cases, you get uninitialized memory, and you can't make any assumptions about its contents.
When the OS has to apportion a new page to your process, it guarantees that it won't expose data from other processes; the usual way to ensure that is to fill it with zeros (but it's equally valid to overwrite with anything else, including even a page worth of /dev/urandom
- in fact some debugging malloc()
implementations write non-zero patterns, to catch mistaken assumptions such as yours).
If malloc()
can satisfy the request from memory already used in this process, its contents won't be cleared (in fact, the clearing is nothing to do with malloc()
and it can't be - it has to happen before the memory is mapped into your address space). You may get memory that has previously been written by your process/program (e.g. before main()
).
answered 1 hour ago
Toby SpeightToby Speight
5,40711133
5,40711133
Your answer is good as far as it goes, +1, but my question has lent itself to misunderstanding. I have edited the question to clarify that I am not asking not about the requirements of the C standard (if I were asking that, I would ask on Stack Overflow) but about the observed behavior of GNU/Linux systems.
– thb
1 hour ago
A week or two ago, I tried a different experiment, callingmalloc()
andfree()
repeatedly. Though nothing requiresmalloc()
to reuse the same storage recently freed, in the experiment,malloc()
did happen to do that. It happened to return the same address each time, but also nulled the memory each time, which I had not expected. This was interesting to me. Further experiments have led to today's question.
– thb
1 hour ago
Uhh, if you call a low enough level call it is indeed guaranteed to be filled with zeros.
– Joshua
43 mins ago
If it interests you, I have added the second experiment to the question.
– thb
33 mins ago
Perhaps I'm not being clear enough - most implementations ofmalloc()
do absolutely nothing with the memory they hand you - it's either previously-used, or freshly-assigned (and therefore zeroed by the OS). In your test, you evidently got the latter. Similarly, the stack memory is given to your process in the cleared state, but you don't examine it far enough to see parts your process hasn't yet touched.
– Toby Speight
16 secs ago
add a comment |
Your answer is good as far as it goes, +1, but my question has lent itself to misunderstanding. I have edited the question to clarify that I am not asking not about the requirements of the C standard (if I were asking that, I would ask on Stack Overflow) but about the observed behavior of GNU/Linux systems.
– thb
1 hour ago
A week or two ago, I tried a different experiment, callingmalloc()
andfree()
repeatedly. Though nothing requiresmalloc()
to reuse the same storage recently freed, in the experiment,malloc()
did happen to do that. It happened to return the same address each time, but also nulled the memory each time, which I had not expected. This was interesting to me. Further experiments have led to today's question.
– thb
1 hour ago
Uhh, if you call a low enough level call it is indeed guaranteed to be filled with zeros.
– Joshua
43 mins ago
If it interests you, I have added the second experiment to the question.
– thb
33 mins ago
Perhaps I'm not being clear enough - most implementations ofmalloc()
do absolutely nothing with the memory they hand you - it's either previously-used, or freshly-assigned (and therefore zeroed by the OS). In your test, you evidently got the latter. Similarly, the stack memory is given to your process in the cleared state, but you don't examine it far enough to see parts your process hasn't yet touched.
– Toby Speight
16 secs ago
Your answer is good as far as it goes, +1, but my question has lent itself to misunderstanding. I have edited the question to clarify that I am not asking not about the requirements of the C standard (if I were asking that, I would ask on Stack Overflow) but about the observed behavior of GNU/Linux systems.
– thb
1 hour ago
Your answer is good as far as it goes, +1, but my question has lent itself to misunderstanding. I have edited the question to clarify that I am not asking not about the requirements of the C standard (if I were asking that, I would ask on Stack Overflow) but about the observed behavior of GNU/Linux systems.
– thb
1 hour ago
A week or two ago, I tried a different experiment, calling
malloc()
and free()
repeatedly. Though nothing requires malloc()
to reuse the same storage recently freed, in the experiment, malloc()
did happen to do that. It happened to return the same address each time, but also nulled the memory each time, which I had not expected. This was interesting to me. Further experiments have led to today's question.– thb
1 hour ago
A week or two ago, I tried a different experiment, calling
malloc()
and free()
repeatedly. Though nothing requires malloc()
to reuse the same storage recently freed, in the experiment, malloc()
did happen to do that. It happened to return the same address each time, but also nulled the memory each time, which I had not expected. This was interesting to me. Further experiments have led to today's question.– thb
1 hour ago
Uhh, if you call a low enough level call it is indeed guaranteed to be filled with zeros.
– Joshua
43 mins ago
Uhh, if you call a low enough level call it is indeed guaranteed to be filled with zeros.
– Joshua
43 mins ago
If it interests you, I have added the second experiment to the question.
– thb
33 mins ago
If it interests you, I have added the second experiment to the question.
– thb
33 mins ago
Perhaps I'm not being clear enough - most implementations of
malloc()
do absolutely nothing with the memory they hand you - it's either previously-used, or freshly-assigned (and therefore zeroed by the OS). In your test, you evidently got the latter. Similarly, the stack memory is given to your process in the cleared state, but you don't examine it far enough to see parts your process hasn't yet touched.– Toby Speight
16 secs ago
Perhaps I'm not being clear enough - most implementations of
malloc()
do absolutely nothing with the memory they hand you - it's either previously-used, or freshly-assigned (and therefore zeroed by the OS). In your test, you evidently got the latter. Similarly, the stack memory is given to your process in the cleared state, but you don't examine it far enough to see parts your process hasn't yet touched.– Toby Speight
16 secs ago
add a comment |
Thanks for contributing an answer to Unix & Linux Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f509232%2fif-the-heap-is-initialized-for-security-then-why-is-the-stack-uninitialized%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
For what it's worth, your most recent code returns different addresses and (occasional) uninitialised (non-zero) data when run on OpenBSD. This obviously does not say anything about the behaviour that you are witnessing on Linux.
– Kusalananda♦
15 mins ago
Please do not change the scope of your question, and do not try to edit it in order to make answers and comments redundant. In C, the "heap" is nothing else but the memory returned by malloc() and calloc(), and only the latter is zeroing out the memory; the
new
operator in C++ (also "heap") is on Linux just a wrapper for malloc(); the kernel doesn't know nor care what the "heap" is.– mosvy
7 mins ago
Your second example is simply exposing an artifact of the malloc implementation in glibc; if you do that repeated malloc/free with a buffer larger than 8 bytes, you will clearly see that only the first 8 bytes are zeroed.
– mosvy
7 mins ago