Following a pointer to a pointer - c

sqlite3_open takes a pointer to a pointer. Id like to trace the address of the second pointer.
E.g: p1(p2(obj))
https://www.sqlite.org/c3ref/open.html
int sqlite3_open(
const char *filename, /* Database filename (UTF-8) */
sqlite3 **ppDb /* OUT: SQLite db handle */
);
What is the syntax to get the address of that pointer in DTrace?
Im using the pid$target::sqlite3_open:return probe to read from the arg1 that was set from the entry probe.
Im currently using:
// Copy pointer bytes from arg1 to kernel, cast to pointer.
(uintptr_t *)copyin(arg1, sizeof(uintptr_t))
Which results in: invalid kernel access in action.
Im on MacOS with SIP enabled, is this the issue?

I may be misunderstanding your question, but what I suspect is that you've misunderstood how sqlite3_open works.
To call sqlite3_open you should have a code that looks like this:
sqlite3 * pDB = NULL;
/* ... */
int result = sqlite3_open("file:database.db", &pDB);
As you see, there's no "pointer to pointer" variable in my code. Instead, sqlite3_ope takes the address of of a pointer variable I allocated on the stack.
To copy that pointer is as simple as:
sqlite3 * pDB2 = pDB
The reason for this is simple:
The sqlite3_open function wants to return two variable, which is impossible in C.
Instead of returning two variables, sqlite3_open returns only one variable directly and returns the second variable indirectly.
In order to return the second, it takes a pointer to a variable of the same type it wants to return. Then, by dereferencing the address and filling in the value, it provides you with the second variable's value.
However, the second variable sqlite3_open returns is a pointer. This is why, in order to return a pointer as a second variable, sqlite3_open requires a pointer to a pointer variable.
Reading the address
In the example above, the pDB variable holds the address for the sqlite3 object (the one allocated by sqlite3_open).
The address, as you know, is simply a number representing a location in the memory. To read the pointer value as a number, simply cast the pointer to a uintptr_t. i.e.:
uintptr_t db_mem_addr_value = (uintptr_t)pDB;
Of course, numbers (and memory addresses) can't be printed as hex strings directly, they need a function that will convert them into hex notation.
Consider that in C you would print the memory address in Hex notation by using printf i.e.,
fprintf(stderr, "%p\n", (void *)pDB);
Using dtrace would be the same. You might want to convert the pointer address to a number, for example, using the lltostr dtrace function:
lltostr((uintptr_t)*(void**)arg1, 16)

Not a dtrace pro, but here are some observations.
uintptr_t is defined to be large enough to hold any pointer converted to an integer. Note that this does not imply that sizeof(uintptr_t) == sizeof(void*). It is perfectly valid (and on some platforms, necessary) for uintptr_t to be strictly larger than a pointer. That means your copyin call might be copying more bytes than are actually there. Try using a size of sizeof(sqlite**) instead.
Also, it's possible that some of OSX's internal protection mechanisms are causing you problems. See the answer on this related question for a good explanation.

Related

Why dereferencing user-space inside get_user is correct?

For example, in the function SYSCALL_DEFINE2 of linux kernel (https://elixir.bootlin.com/linux/v4.15.3/source/arch/alpha/kernel/osf_sys.c#L657):
SYSCALL_DEFINE2(osf_proplist_syscall, enum pl_code, code,
union pl_args __user *, args)
{
long error;
...
get_user(error, &args->set.nbytes)
...
}
Why it isn't need to use copy_from_user on args before using arrow and dot on it?
&args->set.nbytes is an address. It's allowed to compute the address of some member even if you couldn't actually read it — the compiler just adds the offset of the set.nbytes member of union pl_args to the address passed in args. Then it passes that address to get_user, which does the actual copying from userspace.
The get_user() function will do actual copying from user space, so it will take care of the dangerous part. Take a good look at this:
&args->set.nbytes
What this actually does it not dereference any pointer, but just calculate new pointer value. Pointers are really just unsigned integer values. So the line above says: give me address of part of data that is some offset from the start of structure. Start of structure is args and offset is set.nbytes. No actual accessing of user space memory is required to do this.

Why can I pass an integer as a task parameter but can't pass a struct variable?

I was trying to pass a set of parameters to a task. So I created a struct and passed it to my task, like this:
my_type_t parameters_set;
//... assigning values to parameters_set
xTaskCreate( vMyTask, "MyTask", STACK_SIZE, (void*)parameters_set, 2,
&xHandle);
Inside my task I tried to retrieve the struct values doing the following:
my_type_t received_parameters = (my_type_t) pvParameters;
The task creation line triggers the following error while compiling: "cannot convert to a pointer type" and the struct retrieving line triggers the "Conversion to non-scalar type requested" error. I know if I use a pointer instead of the variable itself it will compile, but I can't do this because the function that creates the task will die and the task will have a reference to a variable that does not exist anymore. In the end I'll use a global struct variable.
But what I really would like to understand is why do tasks accept int values (not by reference), and don't accept a typedef struct? For example, the following snippet builds and works without problem:
//Inside the function that creates the task
int x = 0;
xTaskCreate( vMyTask, "MyTask", STACK_SIZE, (void*)x, 2,
&xHandle);
//Inside the Task
int received_parameter = (int) pvParameters;
Thanks in advance!
The code you show is not taking an int value as a parameter. You pass the parameter using (void*)x, which is an expression that converts the int x to a pointer.
The function xTaskCreate accepts a pointer. You have given it a pointer, so the compiler does not complain. However, the pointer you have given it is not a pointer to x (which would be written as &x) but is a pointer converted from the value of x. Although the compiler does not complain, this is generally not the right thing to pass to xTaskCreate.
The reason this is not working with a structure is that (void*)parameters_set is not a proper expression when parameters_set is a structure. This is because C provides for integers to be converted to pointers but not for structures to be converted to pointers. Generally, there is a natural correspondence between integers and pointers: In a “flat” address space, every byte in memory has an address, and those addresses are essentially counts of bytes from the “start” of the memory address space. So an integer can serve as an address and vice-versa. (In other address spaces, the correspondence may be more complicated, but C still allows the conversions, with some rules.) Thus, when you write (void*)x, the compiler converts the integer value in x to a pointer.
There is no such correspondence between structures and pointers, so the C standard does not define any conversion from structures to pointers, and the compiler complains when you write (void*)parameters_set.
What you should be passing for this parameter is the address of some data to be given to the task. If you want the task to have the data in x, you should pass &x. If you want the task to have the dat in parameters_set, you should pass &parameters_set. The & operator takes the address of its operand, which is different from using the (void*) cast, which attempts to convert its operand to a pointer.
According to the RTOS documentation for xTaskCreate, you should not pass the address of a “stack variable,” by which it effectively means, for C, an object with automatic storage duration. In other words, you should not pass an x or parameters_set that is defined within a function without static or (possibly, depending on arrangements made by RTOS and the C implementation; I am unfamiliar with RTOS) _Thread_local. Alternatively, you can allocate space for the parameters using malloc (or, per comment below, pvPortMalloc for RTOS) and pass that to xTaskCreate.

Pointer types in c

I am trying to pass the address of an int[] to a system call. However messages only have fields for char pointers.
I have tried converting the int pointer to a char pointer and back again but the results in the array are not correct.
int pidarray[j];
m.m1_p3 = (char*)pidarray;
Also I tried to store the address of the array as an integer and pass it through a message integer field.
int pidarray[j];
m.m1_i3 = &pidarray;
EDIT:
This will be a system call of my creation, aimed at filling an array with pids and copying the data back to the process hence why I need to pass the address of the array. I call the system call a so,
_syscall(PM_PROC_NR, GETCHILDPIDS, &m);
It fills an array and attempts to copy it back to memory space owned by the calling process. This is done as:
sys_vircopy(SELF, (vir_bytes) &test, who_e,(vir_bytes) m_in.m1_i3, sizeof(test));
where test is a int[].
How can I send the address of a int[] in this format?
In C, arrays are decayed into pointers, in particular when passed as arguments.
So just handle that as a pointer. You may need to change the interface of your new system call (e.g. perhaps give it a pointer and a size, or have a convention saying that pidarray should be 0 terminated).
BTW, it smells like your system call is useless. You can do all that on Linux in user-land, thru existing syscalls(2) using /proc/ (see proc(5)...).

Extern arrays usage causing access violation

I have a
LS_Led* LS_vol_leds[10];
declared in one C module, and the proper externs in the other modules that access it.
In func1() I have this line:
/* Debug */
LS_Led led = *(LS_vol_leds[0]);
And it does not cause an exception. Then
I call func2() in another C module (right after above line), and do the same line, namely:
/* Debug */
LS_Led led = *(LS_vol_leds[0]);`
first thing, and exception thrown!!!
I don't think I have the powers to debug this one on my own.
Before anything LS_vol_leds is initialized in func1() with:
LS_vol_leds[0] = &led3;
LS_vol_leds[1] = &led4;
LS_vol_leds[2] = &led5;
LS_vol_leds[3] = &led6;
LS_vol_leds[4] = &led7;
LS_vol_leds[5] = &led8;
LS_vol_leds[6] = &led9;
LS_vol_leds[7] = &led10;
LS_vol_leds[8] = &led11;
LS_vol_leds[9] = &led12;
My externs look like
extern LS_Led** LS_vol_leds;
So does that lead to disaster and I how do I prevent disaster?
Thanks.
This leads to disaster:
extern LS_Led** LS_vol_leds;
You should try this instead:
extern LS_Led *LS_vol_leds[];
If you really want to know why, you should read Expert C Programming - Deep C Secrets, by Peter Van Der Linden (amazing book!), especially chapter 4, but the quick answer is that this is one of those corner cases where pointers and arrays are not interchangeable: a pointer is a variable which holds the address of another one, whereas an array name is an address. extern LS_Led** LS_vol_leds; is lying to the compiler and generating the wrong code to access LS_vol_leds[i].
With this:
extern LS_Led** LS_vol_leds;
The compiler will believe that LS_vol_leds is a pointer, and thus, LS_vol_leds[i] involves reading the value stored in the memory location that is responsible for LS_vol_leds, use that as an address, and then scale i accordingly to get the offset.
However, since LS_vol_leds is an array and not a pointer, the compiler should instead pick the address of LS_vol_leds directly. In other words: what is happening is that your original extern causes the compiler to dereference LS_vol_leds[0] because it believes that LS_vol_leds[0] holds the address of the pointed-to object.
UPDATE: Fun fact - the back cover of the book talks about this specific case:
So that's why extern char *cp isn't the same as extern char cp[]. I
knew that it didn't work despite their superficial equivalence, but I
didn't know why. [...]
UPDATE2: Ok, since you asked, let's dig deeper. Consider a program split into two files, file1.c and file2.c. Its contents are:
file1.c
#define BUFFER_SIZE 1024
char cp[BUFFER_SIZE];
/* Lots of code using cp[i] */
file2.c
extern char *cp;
/* Code using cp[i] */
The moment you try to assing to cp[i] or use cp[i] in file2.c will most likely crash your code. This is deeply tight into the mechanics of C and the code that the compiler generates for array-based accesses and pointer-based accesses.
When you have a pointer, you must think of it as a variable. A pointer is a variable like an int, float or something similar, but instead of storing an integer or a float, it stores a memory address - the address of another object.
Note that variables have addresses. When you have something like:
int a;
Then you know that a is the name for an integer object. When you assign to a, the compiler emits code that writes into whatever address is associated with a.
Now consider you have:
char *p;
What happens when you access *p? Remember - a pointer is a variable. This means that the memory address associated with p holds an address - namely, an address holding a character. When you assign to p (i.e., make it point to somewhere else), then the compiler grabs the address of p and writes a new address (the one you provide it) into that location.
For example, if p lives at 0x27, it means that reading memory location 0x27 yields the address of the object pointed to by p. So, if you use *p in the right hand side of an assignment, the steps to get the value of *p are:
Read the contents of 0x27 - say it's 0x80 - this is the value of the pointer, or, equivalently, the address of the pointed-to object
Read the contents of 0x80 - this finally gives you *p.
What if p is an array? If p is an array, then the variable p itself represents the array. By convention, the address representing an array is the address of its first element. If the compiler chooses to store the array in address 0x59, it means that the first element of p lives at 0x59. So when you read p[0] (or *p), the generated code is simpler: the compiler knows that the variable p is an array, and the address of an array is the address of the first element, so p[0] is the same as reading 0x59. Compare this to the case for which p is a pointer.
If you lie to the compiler, and tell it you have a pointer instead of an array, the compiler will (wrongly) generate code that does what I showed for the pointer case. You're basically telling it that 0x59 is not the address of an array, it's the address of a pointer. So, reading p[i] will cause it to use the pointer version:
Read the contents of 0x59 - note that, in reality, this is p[0]
Use that as an address, and read its contents.
So, what happens is that the compiler thinks that p[0] is an address, and will try to use it as such.
Why is this a corner case? Why don't I have to worry about this when passing arrays to functions?
Because what is really happening is that the compiler manages it for you. Yes, when you pass an array to a function, a pointer to the first element is passed, and inside the called function you have no way to know if it is a "real" array or a pointer. However, the address passed into the function is different depending on whether you're passing a real array or a pointer. If you're passing a real array, the pointer you get is the address of the first element of the array (in other words: the compiler immediately grabs the address associated to the array variable from the symbol table). If you're passing a pointer, the compiler passes the address that is stored in the address associated with that variable (and that variable happens to be the pointer), that is, it does exactly those 2 steps mentioned before for pointer-based access. Again, note that we're discussing the value of the pointer here. You must keep this separated from the address of the pointer itself (the address where the address of the pointed-to object is stored).
That's why you don't see a difference. In most situations, arrays are passed around as function arguments, and this rarely raises problems. But sometimes, with some corner cases (like yours), if you don't really know what is happening down there, well.. then it will be a wild ride.
Personal advice: read the book, it's totally worth it.

Character Array Pointers and Casts from Integers (Memory Address)

Okay, currently I am writing a kernel for the sake of my resume. While writing my memory management unit I have hit a brick wall.
int address = (int)malloc(sizeof(Test))
consoleWriteString("Variable Address:\n");
consoleWriteInteger(address);
char* f = (void*)address;
consoleWriteString("\nVariable Address:\n");
consoleWriteInteger((int)&f); // Should print off the same as above
Logically the output should be the same for both. Somewhere somthing has gone wrong though. as my output is the following.
Variable Address: 47167
Variable Address: 1065908
After a long period of testing and debugging I finally gave in and decided to ask stack overflow. Also if you spot any syntax errors ignore them. By the way this is all in C, and all functions are custom, including malloc, but I have determined that the error does not lie in that functon, or any other for a fact. I believe this is just me being stupid about pointers and casting but don't laugh at me when it was somthing super simple that I missed.
Thanks yall
&f is the address of f, not the address contained in it! f is on the stack. Its value (the address you first printed) is pointing to the allocated memory.
Think of it this way: You allocate room in memory for some stuff. This memory region has an address. You put the address in a pointer (f), so that it points to that region. But f itself needs to be somewhere in memory in order to hold the value of that address. In this case, f is on the stack, and &f gets the address of f (the container of the original address), not the address that f contains.
As an aside, be very careful casting addresses to int (and back!), since int might not be large enough to hold an address (e.g. on x86-64, depending on your compiler). I believe the correct type to use when you want to use an address as an integer is uintptr_t in stdint.h.
The value of f (which happens to be a pointer) is the same as address (which is also a pointer, but of a different type) - this is what you do in the line
char* f = (void*)address;
But then you print the address of f:
consoleWriteInteger((int)&f);
And that is not the same thing as the value of f... change that line to
consoleWriteInteger((int)f);
and you should be all set.
the first print out is an int, although malloc returns an address, The second is an address casted to an int. do f instead & gets you the address of a value while * dereferences a pointer getting you the value of what is being pointed to.

Resources