Which of the following operation can lead to illegal memory access - c

Consider the following declaration of C code.
int (*p)[10];
If the variable is not initialized to any address then which of the following may give a run-time error?
(p+1)
(p+1)[2]
p[2]
*p[2]
My answer: 2, 3, 4.
As p is not initialized it can contain any garbage value, so
*(p+3), *(p+2), and **(p+2) can lead to illegal memory access.
Given answer: 4.
Their explanation:
We get a runtime error (segmentation fault) if we try to access some invalid memory.
is a pointer to an integer array of size Its declaration has one * (star) and one [ ] (square bracket). If we want to access memory using then we have to use
Either two * (stars)
Or one * (star) and one [ ] (square bracket)
Or two [ ] (square bracket)
Following usages might give runtime errors -
**p
*p[]
p[][]
can 2 and 3 lead to illegal memory accesses?

Generally none and all of them. Everything depends on how you use it.
Exmaples:
If you use it in sizeof it is safe
printf("%zu\n",sizeof((p+1)));
printf("%zu\n",sizeof(((p+1)[2])));
printf("%zu\n",sizeof((p[2])));
printf("%zu\n",sizeof((*p[2])));
2.Any of them can lead to the Undefined Behaviour (which can express itself as memory fault)
p = (p+1); //UB is p is dereferenced
int *x = (p+1)[2]; //x has undermined value
int *y = p[2]; //same as above
*p[2] = 5; //p was not initialized

Related

Can anyone explain to me what actually causes this Segmentation Fault and How to overcome this?

#include <stdio.h>
#define null1 ((void*)0)
#define val ((int)2)
int main() {
int *p;
p = null1;
printf("%p", &p);
//p = (int *)val;
*p = val;
//printf("\n%p", (int*)p);
return 0;
}
Output:
Segmentation fault(core dumped)
I want to assign value of macro to the Pointer.
null1 is a null pointer, its definition #define null1 ((void*)0) is one of the accepted definitions for a null pointer.
val is a macro expanding to the plain integer constant 2. Casting 2 as (int) has no effect, 2 is already an int constant value.
Storing a value to a null pointer has undefined behavior. A segmentation fault is one the possible effect of undefined behavior. A rather useful one since it allows the debugger to point to the problem immediately.
Casting any other integer value as (int *) has undefined behavior too: p = (int *)val; *p = val; is very likely to cause the same segmentation fault, but may have some other unexpected side effects, depending on the target platform specifics: if 2 happens to be the address of the self-destruct trigger port, all bets are off.
Null pointer is a special reserved value of a pointer. A pointer of any type has such a reserved value. Formally, each specific pointer type (int *, char * etc.) has its own dedicated null-pointer value. Conceptually, when a pointer has that null value it is not pointing anywhere.
Void pointer is a specific pointer type - void * - a pointer that points to some data location in storage, which doesn't have any specific type.
Here null1 refers to a void pointer whereas p is a pointer of the type integer.
Hope I was right and this is helpful to you.
Pointers directly point to memory addresses. While each program that we run is allocated a fixed memory area only (called segments). All memory address in this area is referred to using relative memory address.
But pointers have absolute memory addresses in them. Generally, the starting memory addresses are for Operating System's use. When you assign a pointer to some address outside of its segment area then it gives SIGSEGV ( Segmentation Error ).
In your case also, you are trying to assign p an address outside its segment area thus the error.
NULL pointer is an invalid pointer because it points to memory that is not valid to access so that the statment *p = val is not valid, so the Segmentation fault error appears.
To overcome this error you need to assign a valid address of a memory location to the pointer(p), such as defining a variable (var) and assign the addres to the pointer as p = &var;.
You just need to assign your pointer to your macro as code below:
#include <stdio.h>
#define val 2
int main()
{
int *p;
p = (int*)val;
printf("%p\n", &p);
printf("%d\n", p);
return 0;
}

*(p+i) syntax with p as int*

I'm starting a unit on Software Security - in some prelim reading, I've come across the following pointer syntax and I'm not sure I understand.
int x = 20;
int* p = &x;
int k = *(p+1);
What is k in the example?
I know if I have an array like so:
int j[10] = {0};
int k = *(j+1);
such syntax will de-reference the int (system's sizeof(int)) at location 1 of array j.
So how does this work with the non-array example above?
Pointer arithmetics *(p+1) syntax that adds an int to a pointer is equivalent to p[i]. It assumes that p points into a block of a sufficient size, otherwise the dereference causes undefined behavior.
Note that you do not need to initialize a pointer using an array new. You can use an array in the automatic memory, point to an existing array, or even point into the middle of another array, like this:
int data[20] = {0, 1, 2, 3, 4, 5, 6, ...};
int *p = &data[5]; // Point to element at index five
int x = *(p+1); // Access element at index six
Pointer to value is indistinguishable from pointer to first element of an array.
So int*p = &x is pointer to first element of an array where only one element is allocated. Now *(p+1) is accessing second element on that array - which is access element out of bounds of the array and is undefined behavior (Array index out of bound in C). Valid results for such operation range from world destruction to no-op.
One likely case is *(p+1) would refer to whole/part of p itself as usually variables are allocated sequentially. So there is a good chance nothing spectacular to happen and bug will stay unnoticed for some time. Code reviews and generally being concerned about such code is good practice to prevent those.
In the first example you are pointing to the item in memory directly after x. The memory after x will be the address of x, contained in p.
Keep in mind that this is a generalization, this could change depending on a few things, so it is undefined behaviour. Try adding the line:
printf("&x: %p\n p: %p\n&k: %p\n k: %08x\n", &x, p, &k, k);
and running the code in main. This should print out the memory addresses and the hex value of k. It should give you a more clear idea of what exactly is happening in memory.
Note that if you change the order of your variables the output will change. You should get used to how memory works and why this kind of thing is undefined behaviour (and therefore should never be used for anything besides learning purposes).
Here's a code sample I wrote to play with this syntax by breaking an int up into it's constituent bytes.
#include <stdio.h>
int main(void) {
int i = 9;
int* p = &i;
char* c = (char*)&i;
printf("%08X\n", *c);
printf("%08X\n", *(c+1));
printf("%08X\n", *(c+2));
printf("%08X\n", *(c+3));
return 0;
}
➜ tmp git:(master) ✗ ./q3
00000009
00000000
00000000
00000000

Iterating over a 2D array with a single char pointer

While doing some research on multi-dimensional arrays in C and how they're stored in memory I came across this: "Does C99 guarantee that arrays are contiguous?". The top-voted answer states that "It must also be possible to iterate over the whole array with a (char *)," then provides the following "valid" code:
int a[5][5], i, *pi;
char *pc;
pc = (char *)(&a[0][0]);
for (i = 0; i < 25; i++)
{
pi = (int *)pc;
DoSomething(pi);
pc += sizeof(int);
}
The poster then goes on to say that "Doing the same with an (int *) would be undefined behavior, because, as said, there is no array[25] of int involved."
That line confuses me.
Why does using a char pointer constitute as valid / defined behavior while substituting it with an integer pointer doesn't?
Sorry if the answer to my question should be obvious. :(
The difference between using a char* and an int* is strict aliasing rules: If you access (&a[0][0])[6] (i. e. via an int*), the compiler is free to assumes that the access [6] does not leave the array at a[0]. As such, it is free to assumes that (&a[0][0]) + 6 and a[1] + 1 point to different memory locations, even though they don't, and reorder their accesses accordingly.
The char* is a difference because it is explicitly exempted from strict aliasing rules: You can cast anything to a char* and manipulate its bits through this pointer without invoking undefined behavior.
The standard is very clear that if you have:
int a[5];
int* p = &a[0];
Then
p += 6;
is cause for undefined behavior.
We also know that memory allocated for a 2D array such as
int a[5][5];
must be contiguous. Given that, if we use:
int* p1 = &a[0][0];
int* p2 = &a[1][0];
p1+5 is a legal expression and given the layout of a, it is equal to p2. Hence, if we use:
int* p3 = p1 + 6;
why should that not be equivalent to
int* p3 = p2 + 1;
If p2 + 1 is legal expression, why should p1 + 6 not be a legal expression?
From a purely pedantic interpretation of the standard, using p1 + 6 is cause for undefined behavior. However, it is possible that the standard does not adequately address the issue when it comes to 2D arrays.
In conclusion
From all practical points of view, there is no problem in using p1 + 6.
From a purely pedantic point of view, using p1 + 6 is undefined behavior.
Either an int pointer or a char pointer should work, but the operation should differ slightly in these two cases. Assuming sizeof(int) is 4. pc += sizeof(int) moves the pointer 4 bytes forward, but pi += sizeof(int) would move 4 times 4 bytes forward. If you want to use an int pointer, you should use pi ++.
EDIT: sorry about the answer above, using an int pointer does not comply with C99 (although it usually practically works). The reason is explained well in the original question: pointer goes across an array is not well defined in the standard. If you use an int pointer, you would start from a[0], which is a different array from a[1]. In this case, an a[0] int pointer cannot legally (well-defined) point to a[1] element.
SECOND EDIT: Using a char pointer is valid, because the following reason given by the original answer:
the array as a whole must be working when given to memset, memmove or memcpy with the sizeof. It must also be possible to iterate over the whole array with a (char *).
From section 6.5.6 "Additive Operators"
For the purposes of these operators, a pointer to an object that is not an element of an
array behaves the same as a pointer to the first element of an array of length one with the
type of the object as its element type.
So it is reasonable.

Accessing structure elements using pointers

I got surprised when the following program did not crash.
typedef struct _x {
int a;
char b;
int c;
} x;
main() {
x *ptr = 0;
char *d = &ptr->b;
}
As per my understanding the -> operator has higher precedence over & operator. So I expected the program to crash at the below statement when we try to dereference the NULL pointer tr.
char *d = &ptr->b;
But the statement &ptr->b evaluates to a valid address. Could somebody please explain where I'm wrong?
Your expectations were unfounded. C programs don't necessarily "crash" when you dereference null pointers. C programs exhibit so called undefined behavior when you attempt to do something like that. Undefined behavior can manifest itself in many different ways. It can result in a crash. Or it can produce something that even resembles a "working" program. The latter is what apparently happened in your case.
But in any case, your program's behavior is undefined. And no, it does not produce a "valid address" as you seem to mistakingly believe. A numerical address that corresponds to a location in memory where no object exists is not valid (with the exception of null pointer value, of course).
The reason that your code doesn't crash is that you didn't actually dereference the pointer. Notice that the expression
&ptr->b
doesn't actually try loading the contents of ptr or ptr->b. Instead, it just stores the address of where this is located in memory. What you'll end up getting is a pointer to where the b field of the object pointed at by ptr should be. This will be a few bytes past address 0, so dereferencing the pointer you just created will cause a segfault.
&ptr->b == sizeof(int), that means the offset of b within _x after _x.a (which is of type int) relative to the address *((x*)0). The offset of 4 (typical for 32bit architecture) is saved within the d pointer. You have to access to d in order to get an seg-fault.
Computing an address does not require accessing memory. &ptr->b means "give me the address of the b field of the structure pointed to by ptr." Doing so does not require looking at whatever may be stored in that memory location.
It may be helpful to think about indexing an array instead of a structure. C defines ptr[5] as equivalent to *(ptr + 5) , which means that &(ptr[5]) is the same as &(*(ptr + 5)). Now it's easy to see that the & and the * "cancel out" and leave you with (ptr + 5), which involves only a pointer increment, and not a load from memory.
C makes this slightly cloudy because it distinguishes lvalues from rvalues. That is, an expression that refers to memory is treated differently on the left hand side of an expression than it is on the right. Given a statement like x = y;, a C compiler will load a value from the address of y, and store it in the address of x. This is the distinction: y is implicitly dereferenced, but x is not.

Global pointers cause segmentation fault?

When compiled by gcc and then run,
the code
int *p;
int main() {*p = 1;}
causes a segmentation fault.
Apparently, the memory location
contained in p cannot be written to.
Why????
On the other hand,
int q[];
int main() {*q = 1;}
runs just fine.
What's going on here??
Why does p contain only read-only memory?
The first example has a wild (not explicitly initialized) pointer. Since it's not an automatic variable, it is set to 0, which clearly is not memory you own. You can see this by printing it out with:
printf("%p\n", p)
As for the second, C99 §6.9.2 actually gives this as an example:
EXAMPLE 2 If at the end of the
translation unit containing
int i[];
the array i still has incomplete type,
the implicit initializer causes it to
have one element, which is set to zero
on program startup.
In general, objects with tentative definition (no initializer) are initialized with 0, which for an array means a 1-element array with element value 0.
*p = 1; causes Segmentation fault because it was not allocated any memory before the assignment.
*q = 1;works because the compiler (gcc 4.2.1 on Mac OS X) warns that q[] is assumed to have one element.
Your first example causes a segmentation fault because you are dereferencing NULL. You never initialize p with a value, and because it's a global it will be NULL. Thus, you dereference NULL, and boom.
I'm not sure how the second example is valid - gcc notes that it is assuming q to be a 1-element array, which is why that won't blow up.

Resources