Let's assume that there is a function store_at(int) which is supposed to store the passed number in a given hexa location as shown below:
void store_at(int val)
{
int *ptr;
ptr = (int *)0x261;
// logic goes here
return;
}
How do we write logic to store val at the given Hex location (0x261 In this case)?
Does saying *ptr = val; work? I vaguely remember reading somewhere that this is not allowed in C.
*ptr = val; works. But you have to make sure this address is allocated and even more, accessible. Without knowing for what you are programming C, I could suggest some ways of prevention on accessing addresses you don't have permission. So it pretty much depends on the architecture and/or operational system you're using.
For example, in ATMEGA32 microcontroller, you don't have any limitation regarding the access of the main memory for it. You can read, write and execute code from/for it:
PORTB = 1;
// Knowing that PORTB is stored at 0x38, you can do the equivalent:
*((unsigned int *)0x0038) = 1;
But that's on embedded systems. Now if you want total access for a memory space (as long as it's in your application sandbox), you can use VirtualProtect for Windows and mprotect for Linux:
int val = 123;
DWORD oldprotection;
VirtualProtect((LPVOID)0x261, sizeof(int), PAGE_EXECUTE_READWRITE, &oldprotection);
*(int *)0x261 = val;
And here is the types of protection you can use with it: Memory Protection Constants.
And a mprotect example:
int val = 123;
mprotect((const void *)(((int)(0x261) / PAGESIZE) * PAGESIZE), sizeof(int), PROT_WRITE | PROT_READ | PROT_EXEC);
*(int *)0x261 = val;
Note that this mprotect example is untested, you may need to increase the size for protection or some other things.
The division by PAGESIZE there is just a trick to align the address correctly. Also note that your address is invalid for Linux, as its division will lead to 0 if PAGESIZE is greater than it (the same as "it will be").
According to the syntax for accessing a address using a pointer, all of these work:
*(int *)0x261 = val;
int *ptr = (int *)0x261;
*ptr = val;
Yes, expression *ptr = val (and even more, *(int *)0x261 = val; ) is perfectly valid in C. But then you're facing technical limitations of runtime environments.
Modern operating systems usually run processes in a sandbox of virtual memory (so processes can't access and spoil memory of some other process) and technically the virtual memory of a process looks like a set of regions which you can access, some in readonly way, some does not allow executing code from here and so on. When you try to access non-available VM region, you'll get SIGSEGV on Unix-like systems or Access Violation error on Windows systems, the same for writing to a read-only memory region and trying to execute code in region where it's prohibited by operating system (for example, you can see virtual memory mappings for a linux process with pid in /proc/$PID/maps.
Memory of a process is usually managed by the operating system (you get new memory from the heap using OS-provided functions like malloc(), calloc(); the stack memory regions are allocated by the OS at process startup), so in user-space programming you virtually never need to reference data by literal pointer.
Another possible environments are kernel-space or bare-metal C programs, where you have all the physical memory available to you, but still you must be aware of what you accessing (it may be ports, a gap in the physical memory, it may be reserved by hardware and so on). Programming such environments is an advanced topic and needs good C experience.
Related
Say we have program 1...
./program1
int main(int argc, char *argv[])
{
int *i;
*i = 10;
printf("%lld", i);
return 0;
}
Now program 2...
./program2 program1output 10
int main(int argc, char *argv[])
{
int *t;
t = (int*)atoll(argv[1]);
*t = atoi(argv[2]);
return 0;
}
Will this work? Can you share memory addresses between different programs?
This behavior is not defined by the C standard. On any general-purpose multi-user operating system, each process is given its own virtual address space. All of the memory assigned to a process is separate from the memory assigned to other processes except for certain shared memory:
Read-only data may be shared between processes, especially the instructions and constant data of two processes running the same executable and the instructions and constant data of shared libraries. That data may have the same address in different processes or different addresses (depending on various factors, including whether the code is position-independent and whether address space layout randomization is in use).
Some operating systems also map system-wide shared data into processes by default.
Memory may be shared between processes by explicit request of those processes to map shared memory segments. Those segments may or may not appear at the same virtual address in the different processes. (A request to map shared memory may request a certain address, in which case different processes could arrange to use the same address, or it could let the mapping software choose the address, in which case different processes cannot rely on receiving the same address assignment.)
In a special-purpose operating system, different processes could share one address space.
Supplement
This is not correct code:
int *i;
*i = 10;
The declaration int *i; defines i to be a pointer but does not assign it a value. Then using *i is improper because it attempts to refer to where i points, but i has not been assigned to point to anything.
To define an int and make its address visible in output, you could define int i; and then print &i.
This is not the proper way to print an address:
printf("%lld", i);
To print an address, cast it to void * and format it with %p. The result of the formatting is implementation-defined:
printf("%p", (void *) &i);
This is not a good way to reconstruct an address:
int *t;
t = (int*)atoll(argv[1]);
As with printf, the type should be void *, and there are problems attempting the conversion with atoll. The C standard does not guarantee it will work; the format produced by printing with %p might not be a normal integer format. Instead, use the %p specifier with sscanf:
void *temp;
if (1 != sscanf(argv[1], "%p", &temp))
exit(EXIT_FAILURE);
int *t = temp;
When the address comes from other process, the behavior of the sscanf conversion is not defined by the C standard.
In principal, an application operates on its own/private memory. There are ways of sharing memory among different processes, but this requires special mechanism to overcome above mentioned "principal" (memory mapped files, for example). Have a short look at, for example, this article on sharing memory.
In your case, program one will have ended and its memory is not available any more; and the way you access it is definitely not one of the "special mechanisms" necessary to access shared memory:
Though an integer vale may be converted to a pointer value, accessing this pointer is only valid if the integer value has originally been converted from a pointer to a valid object. This is not the case in your example, since the integral value calculated in t = (int*)atoll(argv[1]); never pointed to a valid object in the current program.
In general, memory addresses are tied to processes because each process may have its own memory space. So, the addresses are virtual addresses rather than physical addresses, which means they are references to a location in the process's memory space rather than references to a location on a chip.
(Not all environments have virtual memory. For example, an embedded system might not.)
If you have two programs running in the same process, a pointer can be passed between them. For example, a main program can pass a pointer to a dynamically linked library it loads.
I have this code:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
int main (int argc, char** argv) {
*(volatile uint8_t*)0x12345678u = 1;
int var = *(volatile uint8_t*)0x12345678;
printf("%i", var);
printf("%i", &var);
return (EXIT_SUCCESS);
}
I want to see a 1 and the address of that int, which i specified previously. But when compiled by gcc in bash, only "command terminated" without any error will be shown. Does anyone know why so?
PS: I am newbie to C, so just experimenting.
What you are doing:
*(volatile uint8_t*)0x12345678u = 1;
int var = *(volatile uint8_t*)0x12345678;
is totally wrong.
You have no guarantee whatsoever that an arbitrary address like 0x12345678 will be accessible, not to mention writable by your program. In other words, you cannot set a value to an arbitrary address and expect it to work. It's undefined behavior to say the least, and will most likely crash your program due to the operating system stopping you from touching memory you don't own.
The "command terminated" that you get when trying to run your program happens exactly because the operating system is preventing your program from accessing a memory location it is not allowed to access. Your program gets killed before it can do anything.
If you are on Linux, you can use the mmap function to request a memory page at an (almost) arbitrary address before accessing it (see man mmap). Here's an example program which achieves what you want:
#include <sys/mman.h>
#include <stdio.h>
#define WANTED_ADDRESS (void *)0x12345000
#define WANTED_OFFSET 0x678 // 0x12345000 + 0x678 = 0x12345678
int main(void) {
// Request a memory page starting at 0x12345000 of 0x1000 (4096) bytes.
unsigned char *mem = mmap(WANTED_ADDRESS, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// Check if the OS correctly granted your program the requested page.
if (mem != WANTED_ADDRESS) {
perror("mmap failed");
return 1;
}
// Get a pointer inside that page.
int *ptr = (int *)(mem + WANTED_OFFSET); // 0x12345678
// Write to it.
*ptr = 123;
// Inspect the results.
printf("Value : %d\n", *ptr);
printf("Address: %p\n", ptr);
return 0;
}
The operating system and loader do not automatically make every possible address available to your program. The virtual address space of your process is constructed on demand by various operations of the program loader and of services inside the process. Although every address “exists” in the sense of being a potential address of memory, what happens when a process attempts to access an address is controlled by special data structures in the system. Those data structures control whether a process can read, write, or execute various portions of memory, whether the virtual addresses are currently mapped to physical memory, and whether the virtual addresses are not currently mapped to memory but will be provide with physical memory when needed. Initially, much of a process’ address space is marked not in use (or at least implicitly marked, in that none of the explicit records for the address space apply to it).
In the executions of your program you have attempted so far, the address 0x12345678 has not been mapped and marked available to your process, so, when your process attempted to use it, the system detected a fault and terminated your process.
(Some systems randomize the layout of the address space when a program is being loaded, to make it harder for an attacker to exploit bugs in a program. Because of this, it is possible that 0x12345678 will be accessible in some executions of your program and not others.)
The quote from C11 standard 6.5.3.2p4:
4 The unary * operator denotes indirection. [...] If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.
You use * operator on (volatile uint8_t*)0x12345678u pointer. Is this a valid pointer? Is it invalid pointer? What is an "invalid value" of a pointer?
There is no check that allows to find out which particilar pointer values are valid, which aren't. It is not implemented in C language. A random pointer may just happen to be a valid pointer. But most, most probably it is an invalid pointer. In which case - the behavior is undefined.
Dereferencing an invalid pointer is undefined behavior. But - outside of C scope and into operating system - on *unix systems trying to access memory that you are not allowed to, should raise a signal SIGSEGV on your program and terminate your program. Most probably this is what happens. Your program is not allowed to access memory location that is behind 0x12345678 value, the operating system specifically protects against that.
Also note, that systems use ASLR, so that pointer values within your program are indeed in some degree random. There are not linear, ie. *(char*)0x01 will not access the first byte in your ram. Operating system (or more exact, the underlying hardware as configured by the operating system) translates pointer values in your program to physical location in ram using what is called virtual memory. The same pointer values may just happen to be valid on the second run of your program. But most probably, because pointers can have so many values, most probably it isn't a valid pointer. Your operating system kills your program, as it detects an invalid memory access.
I'm learning Linux C programming recently,and there is a question have puzzled me long time.The question is that when we use malloc to allocate some memory,we can use the addresses that over the size we required,but when we access a large address than we required,the system may kill our process.just like the following codes:
int *p = malloc(10*sizeof(int));
*(p + 10) = 1;
when we use this clause,the system may not kill our process,but when we use:
*(p +10000) = 1;
the system may kill our process.
So why does the system do it in this way?
Actually, you can access memory you didn't allocate.
It's not C that (sometimes) prevents you from doing so (it's designed after the philosophy that the user is always right), it's the operating system using special MMU (memory management unit) hardware.
This hardware cannot (for performance and cost reasons) secure any arbitrary address, but only ranges of memory (pages). Thus illegal (from a programmer's standpoint) accesses are sometimes possible (if they are on the same memory page where you have legally allocated memory), other illegal accesses (outside pages with legal addresses) are prevented by the MMU that issues a page fault (segmentation violation).
This obviously doesn't mean you are allowed to access unallocated memory, it just explains why you sometimes get by with it.
Of course all this is only true for platforms that actually have a MMU. There are still a lot around that haven't, so better learn your lessons ;).
Obviously, you can only access memory you allocated upfront.
So what you're doing basically is:
int *p = malloc(10 * sizeof(int));
Here, you allocate memory for 10 ints.
*(p + 10) = 1;
This can be rewritten as (by removing pointer arithmetic):
p[10] = 1;
Now, you can clearly see that there's no memory for the 10th item. So in theory, your code should crash here already.
However, often, the OS decides to allocate a little more memory or you've allocated a block of memory next to p (in which case the OS won't intervene, even though your code doesn't work the way you want it to).
Let me give you an example:
char foo[4] = "foo";
char bar[4] = "bar";
Now (in theory, obviously it depends on what your compiler does) if you access the 5th char of foo, it actually maps to the first char of bar:
foo[5] = "c";
printf("%s %s\n", foo, bar); /* == foo car */
As I said, this depends on your compiler and I only give you this example because it might help you understand how the OS allocates memory.
One more thing:
The NULL pointer ((void *)0) is "protected" and the OS guarantees that it fails when you try to access the contents of that address.
if you dynamically allocate memory, you are allocating it on the heap. You do this by assigning a pointer the beginning of your memory block. So you can add an offset to this pointer and access a specific memory location. If this offset is large enough your pointer could end up pointing outside of the heap, or outside of your data section, or to a protected memory location. This will cause a segmentation fault - you may be accessing a memory location which is used for something else.
Your question is unclear, but I think you're asking why you "can" access an element outside of what you allocated. The answer is that you really can't. There's no telling what you'll be stepping on when you do that -- perhaps another variable, maybe even your code. For very large offsets, you'll definitely end up outside of the system memory that you "own", and will have your process killed.
It appears that you have no idea what you're doing with malloc(). Go back and read about it again.
I'm trying to Implement simple OS and now have to implement memory management.
At first, we typed simple code code to check memory size as below.
What the problem i met is that the result of this function depends on increment size.
If I set increment to 1024, this function return 640Kb.
However, If I set increment to 1024*1024, this functinon return 120Mb.
(my system(bochs)'s memory set to 120MB.)
I checked the optimization option and A20 gate.
Anyone who knows why my function didn't work well?
unsigned int memtest_sub(unsigned int start, unsigned int end)
{
unsigned int i;
unsigned int* ptr;
unsigned int orgValue;
const unsigned int testValue = 0xbfbfbfbf;
for (i = start; i <= end; i += 1024*1024) {
ptr = (unsigned int*) i;
orgValue = *ptr;
*ptr = testValue;
if (*ptr != testValue) {
break;
}
*ptr = orgValue;
}
return i;
}
You can't do probes like that.
First the memory isn't necessarily contiguous as you've already discovered. It almost never is. The hole at 640k is for legacy reasons, but even further in the memory is usually split up. You have to ask your firmware for the memory layout.
Second some memory banks might be double mapped into the physical space and you'll end up in real trouble if you start using them. This isn't very common, but it's a real pain to deal with it.
Third, and probably most important, there are devices mapped into that space. By writing to random addresses you're potentially writing to registers of important hardware. Writing back whatever you read won't do you good because some hardware registers have side effects as soon as you write them. As a matter of fact, some hardware registers have side effects when you read them. Some of that hardware isn't necessarily protected and you might do permanent damage. I've bricked ethernet hardware in the past by having pointer errors in a 1:1 mapped kernel because the EEPROM/flash was unprotected. Other places you write to might actually change the layout of the memory itself.
Since you're most likely on i386 read this: http://wiki.osdev.org/Detecting_Memory_(x86)
Also, consider using a boot loader that detects memory for you and communicates that and other important information you need to know in a well defined API. The boot loader is better debugged with respect to all weird variants of hardware.
Following assignments are buggy:
ptr = (unsigned int*) i;
orgValue = *ptr;
*ptr = testValue;
ptr not pointing any valid memory, you can't treat i's value as address where you can perform some read-write operation - Undefined behaviour
Is it possible to assign a variable the address you want, in the memory?
I tried to do so but I am getting an error as "Lvalue required as left operand of assignment".
int main() {
int i = 10;
&i = 7200;
printf("i=%d address=%u", i, &i);
}
What is wrong with my approach?
Is there any way in C in which we can assign an address we want, to a variable?
Not directly.
You can do this though : int* i = 7200;
.. and then use i (ie. *i = 10) but you will most likely get a crash. This is only meaningful when doing low level development - device drivers, etc... with known memory addreses.
Assuming you are on an x86-type processer on a modern operating system, it is not possible to write to aribtray memory locations; the CPU works in concert with the OS to protect memory so that one process cannot accidentally (or intentionally) overwrite another processes' memory. Allowing this would be a security risk (see: buffer overflow). If you try to anyway, you get the 'Segmentation fault' error as the OS/CPU prevents you from doing this.
For technical details on this, you want to start with 1, 2, and 3.
Instead, you ask the OS to give you a memory location you can write to, using malloc. In this case, the OS kernel (which is generally the only process that is allowed to write to arbitrary memory locations) finds a free area of memory and allocates it to your process. The allocation process also marks that area of memory as belonging to your process, so that you can read it and write it.
However, a different OS/processor architecture/configuration could allow you to write to an arbitrary location. In that case, this code would work:
#include <stdio.h>
void main() {
int *ptr;
ptr = (int*)7000;
*ptr = 10;
printf("Value: %i", *ptr);
}
C language provides you with no means for "attaching" a name to a specific memory address. I.e. you cannot tell the language that a specific variable name is supposed to refer to a lvalue located at a specific address. So, the answer to your question, as stated, is "no". End of story.
Moreover, formally speaking, there's no alternative portable way to work with specific numerical addresses in C. The language itself defines no features that would help you do that.
However, a specific implementation might provide you with means to access specific addresses. In a typical implementation, converting an integral value Ato a pointer type creates a pointer that points to address A. By dereferencing such pointer you can access that memory location.
Not portably. But some compilers (usually for the embedded world) have extensions to do it.
For example on IAR compiler (here for MSP430), you can do this:
static const char version[] # 0x1000 = "v1.0";
This will put object version at memory address 0x1000.
You can do in the windows system with mingw64 setup in visual studio code tool, here is my code
#include<stdio.h>
int main()
{
int *c;
c = (int *)0x000000000061fe14; // Allocating the address 8-bit with respect to your CPU arch.
*c = NULL; // Initializing the null pointer for allocated address
*c = 0x10; // Assign a hex value (you can assign integer also without '0x')
printf("%p\n",c); // Prints the address of the c pointer variable
printf("%x\n",*c); // Prints the assigned value 0x10 -hex
}
It is tested with mentioned environment. Hope this helps Happy coding !!!
No.
Even if you could, 7200 is not a pointer (memory address), it's an int, so that wouldn't work anyway.
There's probably no way to determine which address a variable will have. But as a last hope for you, there is something called "pointer", so you can modify a value on address 7200 (although this address will probably be inaccessible):
int *i = (int *)7200;
*i = 10;
Use ldscript/linker command file. This will however, assign at link time, not run time.
Linker command file syntax depends largely on specific compiler. So you will need to google for linker command file, for your compiler.
Approximate pseudo syntax would be somewhat like this:
In linker command file:
.section start=0x1000 lenth=0x100 myVariables
In C file:
#pragma section myVariables
int myVar=10;
It's not possible, maybe possible with compiler extensions. You could however access memory at an address you want (if the address is accessible to your process):
int addr = 7200;
*((int*)addr) = newVal;
I think '&' in &a evaluates the address of i at the compile time which i think is a virtual address .So it is not a Lvalue according to your compiler. Use pointer instead