Pointer cast to a plain integral type
ID |
objectivec.correctness.pointer_to_integral_cast |
Severity |
low |
Remediation Complexity |
hard |
Remediation Risk |
medium |
Remediation Effort |
medium |
Resource |
Cast |
Language |
Objective-C |
Description
Reports a cast of a value that is certainly a pointer - self, _cmd, an address-of expression, or the result of a cast to a pointer type - to a plain C integral type such as int, long or size_t. uintptr_t, intptr_t, NSInteger and NSUInteger, the types defined to hold a pointer, are not reported.
Rationale
Nothing in the language says a pointer fits in the integer it is being squeezed into. Where it does not, the address is truncated silently: the cast compiles, the arithmetic looks right, and the value only becomes wrong for allocations above the type’s range - which is why this survives testing on one architecture and fails on the next. int is the sharp case, since it stays 32 bits on the 64-bit runtime where every object address is 64. Even where the widths happen to agree, the result of the conversion is implementation-defined rather than portable, and casting the integer back to a pointer is only guaranteed to round-trip through the types intended for it.
The following code illustrates the pattern detected by this rule:
@implementation Registry
- (void)tagEntries {
// FLAGGED: Pointer cast to a plain integral type
long identity = (long)self;
// FLAGGED: Pointer cast to a plain integral type
int truncatedIdentity = (int)self;
// FLAGGED: Pointer cast to a plain integral type
size_t selectorTag = (size_t)_cmd;
// FLAGGED: Pointer cast to a plain integral type
long backingAddress = (long)&_backing;
// FLAGGED: Pointer cast to a plain integral type
unsigned int bufferTag = (unsigned int)(char *)self.bytes;
NSLog(@"%ld %d %zu %ld %u", identity, truncatedIdentity, selectorTag, backingAddress, bufferTag);
}
@end
Remediation
Use uintptr_t (or intptr_t) from <stdint.h> when an address genuinely has to be held as an integer - both are defined to round-trip through a pointer without loss. On Apple platforms NSUInteger serves the same purpose and reads more naturally in Objective-C, which is why -hash returning (NSUInteger)self is idiomatic. Better still, ask whether the integer is needed at all: an NSValue wrapping the pointer, or an NSMapTable keyed by object identity, keeps the pointer a pointer.