Interjection: why no Linux “IsBadReadPtr”?

It’s amusing to listen to people argue over Windows ‘IsBadWritePtr(p)’ and its cousins. Linux developers seem to get a bit defensive about Windows having a ‘feature’ you don’t find in the standard Linux libraries — and the fun comes from the rant about how you ought to code and test so well that an invalid pointer reference never happens. Ouch. Perhaps it would be better to say,  IsBadWritePtr is far too lenient: it can’t catch freed-malloc pointers, or a host of other problems.

In any case, five minutes of RTFM ought to give most people enough info to write the Linux IsBadWritePtr routine themselves, without any tricks like temporarily catching SIGSEGV. For those of you who prefer to read more than write, (and with apologies for my dense style) here it is:

#include <stdint.h>    // uintptr_t
#include <stdio.h>
#include <unistd.h>     // getpid...
extern int etext;
#define MAXRNGS 100
static struct rng { uintptr_t alpha, omega; } rngv[MAXRNGS], *rend;
void maccess_init()
{
    uintptr_t       brk = (uintptr_t)sbrk(0);
    char            buf[99];
    sprintf(buf, "/proc/%d/maps", getpid());
    FILE            *fp = fopen(buf, "re");
    rend = rngv;
    while (0 < fscanf(fp, "%x-%x %4s %*[^\n]",
                          &rend->alpha, &rend->omega, buf)) {
        if (buf[1] == '-' || rend->alpha < brk)
            continue;
        else if (rend > rngv && rend->alpha == rend[-1].omega)
            rend[-1].omega = rend->omega;
        else if (++rend == rngv+MAXRNGS)
            break;
    }
    fclose(fp);
}

// On my home system, "sbrk(0)" takes about .015 usec.
int maccess(void const *mem, int len)
{
    if ((intptr_t)mem < 0 && (intptr_t)mem + len >= 0)
        return 0;
    if ((char const*)mem + len < (char const*)sbrk(0))
        return mem > (void*)&etext;
    if (!rend)
        maccess_init();
    struct rng *p;
    for (p = rngv; p != rend; ++p)
        if ((uintptr_t)mem + len <= p->omega)
            return (uintptr_t)mem >= p->alpha;
    return 0;
}
Advertisements

About mischasan

I've had the privilege to work in a field where abstract thinking has concrete value. That applies at the macro level --- optimizing actions on terabyte database --- or the micro level --- fast parallel string searches in memory. You can find my documents on production-system radix sort (NOT just for academics!) and some neat little tricks for developers, on my blog https://mischasan.wordpress.com My e-mail sig (since 1976): Engineers think equations approximate reality. Physicists think reality approximates the equations. Mathematicians never make the connection.
This entry was posted in Uncategorized. Bookmark the permalink.

2 Responses to Interjection: why no Linux “IsBadReadPtr”?

  1. ttsiodras says:

    My implementation uses the access system call…

    int isBadMem(void *ptr, size_t size)
    {
    int result = access((const char *)ptr, F_OK);
    if (result == -1 && errno == EFAULT)
    return -1;
    return 0;
    }

    …and works OK for me.

    • mischasan says:

      Cool; a nice (ab)use of the access(2) system call 🙂

      I notice you don’t use the (size) parameter , so that’s really is checking isBadMem(-16 & (uintptr_t)ptr, 16).
      Probing both ends of the range is probably good enough. That would take a second access() call.
      So, in your code, instead of “return 0”:

      return 15 | (uintptr_t) < ptr + size – 1 && 0 < access(size – 1 + (char const*)ptr, F_OK) && errno == EFAULT;

      On my test box, there's also a 20:1 speed difference between the access() implementation and the original post's code, assuming a single access() call per check and assuming the worst case for searching the range table. OTOH, this is an absolute difference between 0.73 usec and 0.03 usec per call, which may hardly be worth worrying about.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s