Sorry if the title isn't as descriptive as it should be, the problem is hard to put in a few words. I am trying to find out how much mem i have available by malloc'ing and if that worked, writing to that segment. On certain systems (all linux on x86_64) i see segfaults when writing to the 2049th mib. The code is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
int main (int argc, char **argv) {
void *addr;
int megasize = 2100
/// allocate the memory via mmap same result
//addr = mmap ((void *) 0, (size_t) megasize << 20, PROT_READ | PROT_WRITE,
// MAP_PRIVATE | MAP_ANONYMOUS, (int) -1, (off_t) 0);
addr = malloc(megasize << 20);
if (addr == MAP_FAILED) {
fprintf (stderr, "alloc of %d megabytes failed %s\n", megasize,
strerror (errno));
exit (1);
};
printf ("got %d megabytes at %p\n", megasize, addr);
{
int i;
char *p = addr;
printf("touching the %d Mb memory:\n", megasize);
for (i = 0; i < megasize; i++) {
p[i << 20] = 0;
putchar('.');
if (i%64==63) // commenting out this line i see that it really is the 2049th mb
printf(" #%d\n", i);
fflush(stdout);
};
putchar('\n');
};
/// free the memory
munmap (addr, (size_t) megasize << 20);
return 0;
}
It segfaults reliably on some systems, whereas on others it works fine. Reading the logs for the systems where it fails tells me it's not the oom killer. There are values for megasize that i can choose which will cause malloc to fail but those are larger.
The segfault occurs reliably for any size bigger than 2gib and smaller than the limit where malloc returns -1 for those systems.
I believe there is a limit i am hitting that isn't observed by malloc and i can't figure out what it is. I tried reading out a few of the limits via getrlimit that seemed relevant like RLIMIT_AS and RLIMIT_DATA but those were way bigger.
This is the relevant part of my valgrindlog
==29126== Warning: set address range perms: large range [0x39436000, 0xbc836000) (defined)
==29126== Invalid write of size 1
==29126== at 0x400AAD: main (in /home/max/source/scratch/memorytest)
==29126== Address 0xffffffffb9436000 is not stack'd, malloc'd or (recently) free'd
Can anybody please give me a clue as to what the problem is?
You'll be getting an overflow when counting via int i, as int is 4 bytes wide here:
p[i << 20] = ...
Change
int i;
to be
size_t i;
size_t is the preferred type when addressing memory.
An 32-bit int cannot store the value 2049 mb. You're invoking undefined behavior via signed integer overflow, and happen to be getting a negative number. On most 32-bit machines, when added to a pointer that wraps back around and ends up giving you the address you wanted, by accident. On 64-bit machines, that gives you an address roughly 2047 mb below the start of your block of memory (or wrapped around to the top of the 64-bit memory space).
Use the proper types. Here, i should have type size_t.
Related
I'm trying to learn about memory and page fault, so I wrote the code below to check my understanding. I don't understand why calling malloc caused MINFL to increase since malloc() shouldn't affect physical memory (from what I understand).
This is my code:
#include <stdio.h>
#include <stdlib.h>
void main() {
printf("Before malloc\n");
getchar();
malloc(1 << 20);
printf("After malloc\n");
getchar();
}
These are the terminal results of ps command.
Before malloc:
After malloc:
There are 2 things I don't understand:
why does MINFL increase?
why does VSZ increase by 1028 and not 1024?
Please help and Thank you.
The answer to both of them is the same and very simple indeed.
As you might know, Glibc malloc will use mmap to directly allocate a block larger than 128 KiB. However, it will need to write bookkeeping information below the pointer - because how else would free know what it should be done when just given a pointer. If you print the pointer that malloc returned, you'll see that it is not page aligned.
Here's a program that demonstrates all this:
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/resource.h>
#define ALLOCATION_SIZE (1 << 20)
int main(void) {
struct rusage usage = {0};
getrusage(RUSAGE_SELF, &usage);
printf("1st before malloc: %lu\n", usage.ru_minflt);
getrusage(RUSAGE_SELF, &usage);
printf("2nd before malloc: %lu\n", usage.ru_minflt);
char *p = malloc(ALLOCATION_SIZE);
printf("pointer returned from malloc: %p\n", p);
getrusage(RUSAGE_SELF, &usage);
printf("after malloc: %lu\n", usage.ru_minflt);
p[0] = 42;
getrusage(RUSAGE_SELF, &usage);
printf("after writing to the beginning of the allocation: %lu\n", usage.ru_minflt);
for (size_t i = 0; i < ALLOCATION_SIZE; i++) {
p[i] = 42;
}
getrusage(RUSAGE_SELF, &usage);
printf("after writing to every byte of the allocation: %lu\n", usage.ru_minflt);
}
outputs something like
1st before malloc: 108
2nd before malloc: 118
pointer returned from malloc: 0x7fbcb32aa010
after malloc: 119
after writing to the beginning of the allocation: 119
after writing to every byte of the allocation: 375
i.e. getrusage and printf cause page faults the first time around, so we call it twice - now the fault count is 118 before the malloc call, and after malloc it is 119. If you look at the pointer, 0x010 is not 0x000 i.e. the allocation is not page-aligned - those first 16 bytes contain bookkeeping information for free so that it knows that it needs to use munmap to release the memory block, and the size of the allocated block!
Now naturally this explains why the size increase was 1028 Ki instead of 1024 Ki - one extra page had to be reserved so that there would be enough space for those 16 bytes! It also explains the source of the page fault - because malloc had to write the bookkeeping information to the copy-on-write zeroed page. This can be proved by writing to the first byte of the allocation - it doesn't cause a page fault any longer.
Finally the for loop will modify the pages and touch the remaining 256 pages out of those 257 mapped in.
And if you change ALLOCATION_SIZE to ((1 << 20) - 16) i.e. allocate just 16 bytes less, you'd see that the both virtual size and the number of page faults would match the values you were expecting.
I've noticed the Linux stack starts small and expands with page faults caused by recursion/pushes/vlas up to size getrlimit(RLIMIT_STACK,...), give or take (defaults to 8MiB on my system).
Curiously though, if I cause page faults by addressing bytes directly, within the limit, Linux will just regularly segfault without expanding the page mapping (no segfault though, if I do it after I had e.g., alloca, cause the stack expansion).
Example program:
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#define CMD "grep stack /proc/XXXXXXXXXXXXXXXX/maps"
#define CMDP "grep stack /proc/%ld/maps"
void vla(size_t Sz)
{
char b[Sz];
b[0]='y';
b[1]='\0';
puts(b);
}
#define OFFSET (sizeof(char)<<12)
int main(int C, char **V)
{
char cmd[sizeof CMD]; sprintf(cmd,CMDP,(long)getpid());
if(system(cmd)) return 1;
for(int i=0; ; i++){
printf("%d\n", i);
char *ptr = (char*)(((uintptr_t)&ptr)-i*OFFSET);
if(C>1) vla(i*OFFSET); //pass an argument to the executable to turn this on
ptr[0] = 'x';
ptr[1] = '\0';
if(system(cmd)) return 1;
puts(ptr);
}
}
What kernel code is doing this? How does it differentiate between natural stack growth and me poking around in the address space?
The linux kernel takes the content of the stack pointer as the limit (within reasonable boundaries). Accessing the stack below the stack pointer minus 65536 and the size for 32 unsigned longs is causing a segmentation violation. So, if you access the memory down the stack you have to make sure, that the stack pointer somehow decreases with the accesses to have the linux kernel enlarge the segment. See this snippet from /arch/x86/mm/fault.c:
if (sw_error_code & X86_PF_USER) {
/*
* Accessing the stack below %sp is always a bug.
* The large cushion allows instructions like enter
* and pusha to work. ("enter $65535, $31" pushes
* 32 pointers and then decrements %sp by 65535.)
*/
if (unlikely(address + 65536 + 32 * sizeof(unsigned long) < regs->sp)) {
bad_area(regs, sw_error_code, address);
return;
}
}
The value of the stack pointer register is key here!
P.S.: I have almost all questions for "invalid next size" but they are not helping me because I do not have another piece of code which malloc or realloc, so that is ruled out. Also, I am not assigning beyond the limits of memory space so I am fine on that front as well.
I have below code which is giving me - *** Error in./server_issue': realloc(): invalid next size: 0x08249170 ***`.
I am not able to identify the problem, I am allocating just 64 bytes at a time so I am not out of memory address space and there is other memory allocation which could have possibly corrupted my heap memory.
Error says, invalid next size but as my last log tells Reallocating 64 bytes, Aborted (core dumped) so it means I am still trying to allocate 64 bytes. So, why error?
For HTML file, have any file which is more than 256 bytes.
Code: (minimum code to reproduce the issue)
#include<stdio.h>
#include<stdlib.h>
#include <stdbool.h>
#include <string.h>
typedef char BYTE;
bool load(FILE*, BYTE**, size_t*);
int main(void)
{
FILE* file = fopen("/home/university/psets/pset6/pset6_working/public/hello.html", "r");
BYTE* content;
size_t length;
load(file, &content, &length);
}
bool load(FILE* file, BYTE** content, size_t* length)
{
int totalLength = 0;
int readBytes = 0;
*content = NULL;
BYTE* fileContentTemp[64]; // working with 222222
while ((readBytes = fread(fileContentTemp, 1, 64, file)) > 0)
{
printf("Reallocating %d bytes, ", readBytes);
*content = realloc(*content, readBytes);
printf("%p\n", *content);
if(totalLength != 0)
{
memcpy(*content + totalLength + 1, fileContentTemp, readBytes);
} else{
memcpy(*content + totalLength, fileContentTemp, readBytes);
}
totalLength = totalLength + readBytes;
}
*length = totalLength;
printf("CC image: %s\n", *content);
printf("length is %d\n", *length);
printf("fileContent %p\n", *content);
return true;
}
Output:
Reallocating 64 bytes, 0x8249170
Reallocating 64 bytes, 0x8249170
*** Error in `./server_issue': realloc(): invalid next size: 0x08249170 ***
Reallocating 64 bytes, Aborted (core dumped)
Update:
Even if I allocate 64 bytes instead of using readBytes while realloc then also I get the same error. I am getting error because of this line - *content = realloc(*content, readBytes);
Also, I am not assigning beyond the limits of memory space so I am fine on that front as well.
Being overly sure of your code is a great way to make debugging more difficult. Suspect everything is wrong.
You're not allocating enough memory, because you're using realloc incorrectly.
p = realloc(p, n) doesn't add n bytes to the allocation, it changes the total allocation size to the size you tell it.
Each time through the loop you're passing readBytes which is the number of additional bytes you want to copy. So your buffer never grows. But then you proceed to write past the end of it:
memcpy(*content + totalLength, ...
as totalLength keeps growing throughout the loop.
Calling realloc in a loop like this is typically a bad idea performance-wise. You should allocate enough total space up-front, and then read into it in the loop.
To get the total file size:
size_t get_file_size(FILE *f)
{
long oldpos;
size_t result;
// Save the original file position
oldpos = ftell(f);
// Seek to the first byte past the end of the file
fseek(f, 0, SEEK_END);
// Get the file position - the number of bytes in the file
result = ftell(f);
// Return the file position to where it was
fseek(f, oldpos, SEEK_SET);
return result;
}
Here's a test, to demostrate what I am saying about realloc. Note, this uses malloc_usable_size which is bad practice, in general.
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
int main(void)
{
int i;
char *p = NULL;
for (i = 0; i < 10; i++) {
p = realloc(p, 16);
printf("[%d] size: %zd\n", i, malloc_usable_size(p));
}
return 0;
}
Output:
$ gcc -Wall -Werror -o realloc realloc.c
$ ./realloc
[0] size: 24
[1] size: 24
[2] size: 24
[3] size: 24
[4] size: 24
[5] size: 24
[6] size: 24
[7] size: 24
[8] size: 24
[9] size: 24
Note that malloc_usable_size() returns the same value, even after multiple calls to realloc which pass the same size. Also, the fact that it returns 24, even though we allocated 16 bytes is expected, and is a result of implementation details of the allocator.
From the man page for malloc_usable_size:
NOTES
The value returned by malloc_usable_size() may be greater than the requested size of the allocation because of alignment and minimum size constraints. Although the excess bytes can be overwritten by the application without ill effects, this is not good programming practice: the number of excess bytes in an allocation depends on the underlying implementation.
The main use of this function is for debugging and introspection.
In assembly language we have instructions like:
movl ax, [1000]
This allows us to access specific memory locations.
But in C can we do something similar to this?
I know inline assembly code using asm() will allow you to do this,
but I would like to know about some C specific technique to achieve this.
I tried the following code and got segmentation error:
int *ptr=0xFE1DB124;
*ptr;
This again was confusing as the memory location was identified by the code given below:
int var;
printf("\nThe Address is %x",&var);
So the memory location is available, but I am still getting a segmentation fault.
Why?
Common C compilers will allow you to set a pointer from an integer and to access memory with that, and they will give you the expected results. However, this is an extension beyond the C standard, so you should check your compiler documentation to ensure it supports it. This feature is not uncommonly used in kernel code that must access memory at specific addresses. It is generally not useful in user programs.
As comments have mentioned, one problem you may be having is that your operating system loads programs into a randomized location each time a program is loaded. Therefore, the address you discover on one run will not be the address used in another run. Also, changing the source and recompiling may yield different addresses.
To demonstrate that you can use a pointer to access an address specified numerically, you can retrieve the address and use it within a single program execution:
#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>
int main(void)
{
// Create an int.
int x = 0;
// Find its address.
char buf[100];
sprintf(buf, "%" PRIuPTR, (uintptr_t) &x);
printf("The address of x is %s.\n", buf);
// Read the address.
uintptr_t u;
sscanf(buf, "%" SCNuPTR, &u);
// Convert the integer value to an address.
int *p = (int *) u;
// Modify the int through the new pointer.
*p = 123;
// Display the int.
printf("x = %d\n", x);
return 0;
}
Obviously, this is not useful in a normal program; it is just a demonstration. You would use this sort of behavior only when you have a special need to access certain addresses.
For accessing Specific memory from user space, we have to map the memory Address to Programs Virtual Address using mmap(), the below C code shows the implementation:
Take a file "test_file" containing "ABCDEFGHIJ".
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
int main(void)
{
char *map_base_addr; // Maping Base address for file
int fd; // File descriptor for open file
int size = 10;
fd= open("test_file", O_RDWR); //open the file for reading and writing
map_base_addr= mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);// Maping file into memory
char *ch= map_base_addr;
int i;
/*Printing first 10 char*/
for(i=0; i<size; i++)
fputc(*(ch+i),stdout);
printf("\n");
*(ch+1) = 'b';
*(ch+4) = 'z';
*(ch+7) = 'x';
/*Printing char after modification*/
for(i=0; i<size; i++)
fputc(*(ch+i),stdout);
printf("\n");
/* Finally unmap the file. This will flush out any changes. */
munmap(map_base_addr, size);
exit(0);
}
The output will be:
ABCDEFGHIJ
AbCDzFGxIJ
It works for me:
#include <stdio.h>
int main(int argc, char**argv) {
int var = 7456;
printf("Adress of var = %x, var=%d\n", &var, var);
int *ptr = (int*)0x22cd28;
printf(" ptr points to %x\n", ptr);
*ptr = 123;
printf("New value of var=%d\n", var);
return 0;
}
Program output:
Adress of var = 22cd28, var=7456
ptr points to 22cd28
New value of var=123
Note:
The address is usually not the same on every execution. When I tried my example I had to run it three times before I got the address to match.
char* can point to any adress (because sizeof (char) = 1). Pointers to larger objects must often be aligned on even adresses (usually one divisible by 4).
Your question doesn't really make much sense if you are running on linux/windows/mac/whatever
http://en.wikipedia.org/wiki/Virtual_memory
You can do that only if you are programming a device without virtual memory, or if you are programming the operating system itself.
Otherwise the addresses you see are not the "real" addresses on the RAM, the operating system translates them to real addresses and if there is not a map to translate your virtual address to a real one, then you can get a segmentation fault. Keep in mind that there are other reasons that can cause a segmentation fault.
[I've solved this problem--please see my last comment below.]
In my application, I need to use my own special malloc, based on Doug Lea's dlmalloc: I map an anonymous file (using mmap), create an mspace out of part of the mapped file, and pass the mspace to mspace_malloc. I am finding that some of the addresses that mspace_malloc returns are not within the bounds of the mapped file--even though, as far as I can tell, the process can write to and read from the malloc'ed memory just fine. Why am I encountering this behavior, and what can I do to force mspace_malloc to return an address within the range of the mspace?
/* Inside dl_malloc.c */
#define ONLY_MSPACES 1
#define MSPACES 1
void * heap;
off_t heap_length;
mspace ms;
void init(size_t size) {
heap = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (heap == MAP_FAILED) {
perror("init: mmap");
exit(EXIT_FAILURE);
}
heap_length = size;
ms = create_mspace(heap, size, 0);
mspace_track_large_chunks(ms, 1);
}
void * my_malloc(size_t bytes) {
return mspace_malloc(heap, bytes);
}
/************************/
/* In application */
#include <stdio.h>
#include <stdlib.h>
#define HEAP_SIZE 0x10000 // 32 pages
#define ROWS 2
#define COLS 4096
extern void init(void);
extern void * my_malloc(size_t bytes);
extern void * heap;
extern off_t heap_length;
int main(void) {
init(HEAP_SIZE);
int ** matrix = (int **)my_malloc(sizeof(int *) * ROWS);
int i;
for (i = 0; i < ROWS; ++i)
matrix[i] = (int *)my_malloc(sizeof(int) * COLS);
printf("Heap bounds: %lx to %lx\n",
(off_t)heap, (off_t)heap + heap_length);
printf("Matrix: %p ", matrix;
for (i = 0; i < ROWS; ++i)
printf("Matrix[%d]: %p ", i, matrix[i]");
printf("\n");
return EXIT_SUCCESS;
}
When I run this program (well, the above is a simplification, but not by much), I see that the address assigned to matrix is within the bounds printed for the heap, but that the two addresses for the two rows are very far below the lower bound--more than 0x100000000 below it! And yet I seem to be able to read and write to the matrix. This last point I find puzzling and would like to understand, but the more urgent issue is that I need to do something to make sure that all the addresses that my_malloc returns are within the heap bounds, because other parts of my application need this.
BTW, note that I do not need to lock my call to create_mspace, since I'm only using one thread in this program. Anyway, I tried setting this argument to 1, but I saw no difference in the results.
Thanks!
Eureka (hahaha)! The above, simplified example, with the given constants, will work correctly, and the returned addresses will be within range. However, if you call my_malloc on sizes that are too large relative to the original mspace, malloc will call mmap (unless you explicitly disable this). The addresses I was seeing were merely those returned as a result of those calls to mmap. So the solution to this mystery turned out to be rather simple. I'm leaving this posted in case others happen to run into this issue and forget about malloc's calls to mmap.