Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C - Why cast to uintptr_t vs char* when doing pointer arithmetic

I am working on a programm where I have to modify the target process memory/ read it.

So far I am using void* for storing adresses and cast those to char* if I need to change them (add offset or modify in general)

I have heard of that type defined in stdint.h but I don't see the difference in using it for pointer arithmetic over the char* conversion (which seems more C89 friendly atleast to me)

So my question: What of those both methods should I use for pointer arithmetic? Should I even consider using uintptr_t over char* in any case?

EDIT 1

Basically I just need to know if this yields

0x00F00BAA hard coded memory adress in target
process
void* x = (void*)0x00F00BAA;
char* y = (void*)0x00F00BAA;
x = (uintptr_t)x + 0x123;
y = (char*)y + 0x123;

x == y?
x == (void*)0x00F00CCD?
y == (void*)0x00F00CCD?
like image 635
Murad Babayev Avatar asked Apr 14 '17 20:04

Murad Babayev


People also ask

Why can't we perform arithmetic on a void * pointer?

Since void is an incomplete type, it is not an object type. Therefore it is not a valid operand to an addition operation. Therefore you cannot perform pointer arithmetic on a void pointer.

Is Uintptr_t a pointer?

Other useful types provided by <inttypes. h> include signed and unsigned integer types large enough to hold a pointer. These are given as intptr_t and uintptr_t .

What is the point of pointer arithmetic?

One usually uses pointer arithmetic when they want to get a pointer again. To get a pointer while using an array index: you are 1) calculating the pointer offset, then 2) getting the value at that memory location, then 3) you have to use & to get the address again.


1 Answers

In comments user R.. points out that the following is likely incorrect if the addresses the code is dealing with are not valid within the current process. I've asked the OP for clarification.

Do not use uintptr_t for pointer arithmetic if you care about the portability of your code. uintptr_t is an integer type. Any arithmetic operations on it are integer arithmetic, not pointer arithmetic.

If you have a void* value and you want to add a byte offset to it, casting to char* is the correct approach.

It's likely that arithmetic on uintptr_t values will work the same way as char* arithmetic, but it absolutely is not guaranteed. The only guarantee that the C standard provides is that you can convert a void* value to uintptr_t and back again, and the result will compare equal to the original pointer value.

And the standard doesn't guarantee that uintptr_t exists. If there is no integer type wide enough to hold a converted pointer value without loss of information, the implementation just won't define uintptr_t.

I've actually worked on systems (Cray vector machines) where arithmetic on uintptr_t wouldn't necessarily work. The hardware had 64-bit words, with a machine address containing the address of a word. The Unix-like OS needed to support 8-bit bytes, so byte pointers (void*, char*) contained a word address with a 3-bit offset stored in the otherwise unused high-order 3 bits of the 64-bit word. Pointer/integer conversions simply copied the representation. The result was that adding 1 to a char* pointer would cause it to point to the next byte (with the offset handled in software), but converting to uintptr_t and adding 1 would cause it to point to the next word.

Bottom line: If you need pointer arithmetic, use pointer arithmetic. That's what it's for.

(Incidentally, gcc has an extension that permits pointer arithmetic on void*. Don't use it in portable code. It also causes some odd side effects, like sizeof (void) == 1.)

like image 156
Keith Thompson Avatar answered Sep 27 '22 21:09

Keith Thompson