A veces queremos capturar a SIGSEGV
para averiguar si un puntero es válido, es decir, si hace referencia a una dirección de memoria válida. (O incluso verifique si algún valor arbitrario puede ser un puntero).
Una opción es verificarlo con isValidPtr()
(funcionó en Android):
int isValidPtr(const void*p, int len) {
if (!p) {
return 0;
}
int ret = 1;
int nullfd = open("/dev/random", O_WRONLY);
if (write(nullfd, p, len) < 0) {
ret = 0;
}
close(nullfd);
return ret;
}
int isValidOrNullPtr(const void*p, int len) {
return !p||isValidPtr(p, len);
}
Otra opción es leer los atributos de protección de la memoria, que es un poco más complicado (funcionó en Android):
re_mprot.c:
#include <errno.h>
#include <malloc.h>
#include "dlog.h"
#include "stdlib.h"
#include "re_mprot.h"
struct buffer {
int pos;
int size;
char* mem;
};
char* _buf_reset(struct buffer*b) {
b->mem[b->pos] = 0;
b->pos = 0;
return b->mem;
}
struct buffer* _new_buffer(int length) {
struct buffer* res = malloc(sizeof(struct buffer)+length+4);
res->pos = 0;
res->size = length;
res->mem = (void*)(res+1);
return res;
}
int _buf_putchar(struct buffer*b, int c) {
b->mem[b->pos++] = c;
return b->pos >= b->size;
}
void show_mappings(void)
{
DLOG("-----------------------------------------------\n");
int a;
FILE *f = fopen("/proc/self/maps", "r");
struct buffer* b = _new_buffer(1024);
while ((a = fgetc(f)) >= 0) {
if (_buf_putchar(b,a) || a == '\n') {
DLOG("/proc/self/maps: %s",_buf_reset(b));
}
}
if (b->pos) {
DLOG("/proc/self/maps: %s",_buf_reset(b));
}
free(b);
fclose(f);
DLOG("-----------------------------------------------\n");
}
unsigned int read_mprotection(void* addr) {
int a;
unsigned int res = MPROT_0;
FILE *f = fopen("/proc/self/maps", "r");
struct buffer* b = _new_buffer(1024);
while ((a = fgetc(f)) >= 0) {
if (_buf_putchar(b,a) || a == '\n') {
char*end0 = (void*)0;
unsigned long addr0 = strtoul(b->mem, &end0, 0x10);
char*end1 = (void*)0;
unsigned long addr1 = strtoul(end0+1, &end1, 0x10);
if ((void*)addr0 < addr && addr < (void*)addr1) {
res |= (end1+1)[0] == 'r' ? MPROT_R : 0;
res |= (end1+1)[1] == 'w' ? MPROT_W : 0;
res |= (end1+1)[2] == 'x' ? MPROT_X : 0;
res |= (end1+1)[3] == 'p' ? MPROT_P
: (end1+1)[3] == 's' ? MPROT_S : 0;
break;
}
_buf_reset(b);
}
}
free(b);
fclose(f);
return res;
}
int has_mprotection(void* addr, unsigned int prot, unsigned int prot_mask) {
unsigned prot1 = read_mprotection(addr);
return (prot1 & prot_mask) == prot;
}
char* _mprot_tostring_(char*buf, unsigned int prot) {
buf[0] = prot & MPROT_R ? 'r' : '-';
buf[1] = prot & MPROT_W ? 'w' : '-';
buf[2] = prot & MPROT_X ? 'x' : '-';
buf[3] = prot & MPROT_S ? 's' : prot & MPROT_P ? 'p' : '-';
buf[4] = 0;
return buf;
}
re_mprot.h:
#include <alloca.h>
#include "re_bits.h"
#include <sys/mman.h>
void show_mappings(void);
enum {
MPROT_0 = 0,
MPROT_R = PROT_READ,
MPROT_W = PROT_WRITE,
MPROT_X = PROT_EXEC,
MPROT_S = FIRST_UNUSED_BIT(MPROT_R|MPROT_W|MPROT_X),
MPROT_P = MPROT_S<<1,
};
unsigned int read_mprotection(void* addr);
int has_mprotection(void* addr, unsigned int prot, unsigned int prot_mask);
#define mprot_tostring(x) ( _mprot_tostring_( (char*)alloca(8) , (x) ) )
char* _mprot_tostring_(char*buf, unsigned int prot);
PD DLOG()
es printf()
el registro de Android. FIRST_UNUSED_BIT()
se define aquí .
PPS Puede que no sea una buena idea llamar a alloca () en un bucle; es posible que la memoria no se libere hasta que la función regrese.