[comp.unix.wizards] Invalid Pointers

jbm@eos.UUCP (Jeffrey Mulligan) (07/17/89)

It has been pointed out that there should be no assumptions
about what addresses are valid; is there any way to get
a guaranteed INVALID address?

I commonly do this sort of thing:

struct foobar { /* some stuff */};

static struct foobar *fb1=NO_FOOBAR;

struct foobar *get_a_foobar()
{
	if( can_allocate_memory_and_initialize )
		return( a_pointer_to_a_foobar );
	else
		return( NO_FOOBAR );
}



	/* somewhere else */

	if( fb1==NO_FOOBAR )	/* initial state */
		if( (fb1=get_a_foobar()) == NO_FOOBAR )
			complain_loudly();




So, the question is, how should NO_FOOBAR be defined?

#define NO_FOOBAR ((struct foobar *) -1 )	is what I use, but...

I note that malloc(3) returns NULL (0) on failure [on the sun], indicating
that 0 could never be a valid address returned from malloc.

Sorry if this is the wrong newsgroup.


-- 

	Jeff Mulligan (jbm@aurora.arc.nasa.gov)
	NASA/Ames Research Ctr., Mail Stop 239-3, Moffet Field CA, 94035
	(415) 694-6290

scs@adam.pika.mit.edu (Steve Summit) (07/17/89)

In article <4348@eos.UUCP> jbm@eos.UUCP (Jeffrey Mulligan) writes:
>It has been pointed out that there should be no assumptions
>about what addresses are valid; is there any way to get
>a guaranteed INVALID address?
>I commonly do this sort of thing:
>
>static struct foobar *fb1=NO_FOOBAR;
>
>So, the question is, how should NO_FOOBAR be defined?

NULL, or the equivalent 0, is precisely the right thing to use
in this situation.  The null pointer is, by definition,
guaranteed not to point to any object.

Using the intermediate NO_FOOBAR is still a good idea, by the
way, because it helps insulate the calling application from the
implementation details of the "foobar" code.  For instance, my
programs contain things like

	#include "graph.h"

	graphd gd = grph_alloc(...);

	if(gd == NOGRAPH)
		...

where graphd is a "graph descriptor" type defined with a typedef
in graph.h .  The caller is not supposed to know what kind of a
type graphd is (that's why it's a typedef).  If a graphd is a
pointer, NOGRAPH is 0, but if it's a small-integer descriptor
(like Unix file descriptors) NOGRAPH is -1.

>Sorry if this is the wrong newsgroup.

Actually, it is, and I'd redirect followups to comp.lang.c,
except that they're generally pretty tired of the NULL topic over
there.  (And yes, unadorned 0 is a guaranteed acceptable
#definition for NULL, so let's not start that discussion here.)

                                            Steve Summit
                                            scs@adam.pika.mit.edu

smb@ulysses.homer.nj.att.com (Steven M. Bellovin) (07/17/89)

In article <4348@eos.UUCP>, jbm@eos.UUCP (Jeffrey Mulligan) writes:
> It has been pointed out that there should be no assumptions
> about what addresses are valid; is there any way to get
> a guaranteed INVALID address?
> 
> I commonly do this sort of thing:
> 
> struct foobar { /* some stuff */};
> 
> static struct foobar *fb1=NO_FOOBAR;
....
> So, the question is, how should NO_FOOBAR be defined?

From the C Reference Manual, section 7.7, ``Equality Operators'':

	A pointer may be compared to an integer only if the integer is
	the constant 0.  A pointer to which 0 has been assigned is
	guaranteed not to point to any object and will appear to be
	equal to 0.  In conventional usage, such a pointer is
	considered to be null.

In other words, NO_FOOBAR should be defined as 0.  And this is a strong
hint to those who think that dereferencing 0 is legal.


		--Steve Bellovin

bzs@bu-cs.BU.EDU (Barry Shein) (07/17/89)

From: jbm@eos.UUCP (Jeffrey Mulligan)
>It has been pointed out that there should be no assumptions
>about what addresses are valid; is there any way to get
>a guaranteed INVALID address?
>
>I commonly do this sort of thing:
>
>struct foobar { /* some stuff */};
>
>static struct foobar *fb1=NO_FOOBAR;

>...

>So, the question is, how should NO_FOOBAR be defined?
>
>#define NO_FOOBAR ((struct foobar *) -1 )	is what I use, but...

A perfectly good solution is:

	struct foobar no_foobar;
	#define NO_FOOBAR (&no_foobar)

being as no_foobar will be the only thing assigned this address it is
unique and solves your problem. This of course works for your example
where you test for a return value of NO_FOOBAR, doing it by causing a
signal to occur (eg. SEGV) is another problem entirely.
-- 
	-Barry Shein

Software Tool & Die, Purveyors to the Trade
1330 Beacon Street, Brookline, MA 02146, (617) 739-0202
Internet: bzs@skuld.std.com
UUCP:     encore!xylogics!skuld!bzs or uunet!skuld!bzs

debra@alice.UUCP (Paul De Bra) (07/17/89)

In article <4348@eos.UUCP> jbm@eos.UUCP (Jeffrey Mulligan) writes:
}
}It has been pointed out that there should be no assumptions
}about what addresses are valid; is there any way to get
}a guaranteed INVALID address?
}...
}So, the question is, how should NO_FOOBAR be defined?
}
}#define NO_FOOBAR ((struct foobar *) -1 )	is what I use, but...
}
}I note that malloc(3) returns NULL (0) on failure [on the sun], indicating
}that 0 could never be a valid address returned from malloc.

According to the (new) K&R book NULL (0) is indeed guaranteed not to be
a valid data (or text or stack) address.
Furthermore, the only meaningful comparisons between pointers are
comparison between pointers to elements of the same string and comparison
between a pointer and NULL.

Paul.
-- 
------------------------------------------------------
|debra@research.att.com   | uunet!research!debra     |
------------------------------------------------------

gwyn@smoke.BRL.MIL (Doug Gwyn) (07/17/89)

In article <4348@eos.UUCP> jbm@eos.UUCP (Jeffrey Mulligan) writes:
-It has been pointed out that there should be no assumptions
-about what addresses are valid; is there any way to get
-a guaranteed INVALID address?

A null pointer is guaranteed not to match any valid object address.

-#define NO_FOOBAR ((struct foobar *) -1 )	is what I use, but...

This is not portable.  Just use NULL.

richm@cbnewsm.ATT.COM (richard.a.miani) (07/17/89)

Why is this discussion on dereferencing NULL pointers in comp.UNIX.wizards and
not in comp.lang.c ?

Rich
-- 
Rich Miani		                        ram@lcuxlm.att.com
				     
Other Paths:     ram%lcuxlm@research.att.com   ...arpa!lcuxlm!ram   

jik@athena.mit.edu (Jonathan I. Kamens) (07/21/89)

In article <34785@bu-cs.BU.EDU> bzs@bu-cs.BU.EDU (Barry Shein) writes:
>A perfectly good solution is:
>
>	struct foobar no_foobar;
>	#define NO_FOOBAR (&no_foobar)
>
>being as no_foobar will be the only thing assigned this address it is
>unique and solves your problem.

struct foobar no_foobar;
#define NO_FOOBAR (&no_foobar)
struct foobar foo_array[50];
struct foobar *ptr;

...

ptr = &foo_array[0];
ptr = ptr - 1;

Now, the last line above is obviously not something that should be
encouraged :-), but fencepost errors of that type happen commonly
inside loops et al.  You can't assume that just because you've
allocated a particular spot in memory be declaring a variable to sit
in that spot, nothing else will ever try to access that spot.

The already-mentioned assignment of NO_FOOBAR to 0 is a better
solution.

Jonathan Kamens			              USnail:
MIT Project Athena				432 S. Rose Blvd.
jik@Athena.MIT.EDU				Akron, OH  44320
Office: 617-253-4261			      Home: 216-869-6432

bzs@bu-cs.BU.EDU (Barry Shein) (07/21/89)

>In article <34785@bu-cs.BU.EDU> bzs@bu-cs.BU.EDU (Barry Shein) writes:
>>A perfectly good solution is:
>>
>>	struct foobar no_foobar;
>>	#define NO_FOOBAR (&no_foobar)
>>
>>being as no_foobar will be the only thing assigned this address it is
>>unique and solves your problem.
>
>struct foobar no_foobar;
>#define NO_FOOBAR (&no_foobar)
>struct foobar foo_array[50];
>struct foobar *ptr;
>
>...
>
>ptr = &foo_array[0];
>ptr = ptr - 1;
>
>Now, the last line above is obviously not something that should be
>encouraged :-), but fencepost errors of that type happen commonly
>inside loops et al.  You can't assume that just because you've
>allocated a particular spot in memory be declaring a variable to sit
>in that spot, nothing else will ever try to access that spot.
>
>The already-mentioned assignment of NO_FOOBAR to 0 is a better
>solution.
>
>Jonathan Kamens			              USnail:

The issue is a *unique* spot to test against versus one which is
guaranteed to cause a core dump or some other fault, perhaps. Although
in theory a pointer of zero should cause some sort of fault we all
should know by now that some of the most popular unix platforms will
treat it as legally as any other address, does the person want the
theoretical answer or one which will work for his/her software on a
variety of platforms? In fact as I remember this is exactly what the
original question was alluding to, he was well aware of using a zero
pointer but felt he needed some alternative (he was using -1.)

Some people suggested just calling malloc(n) and using that and that
indeed is equivalent to my suggestion other than set-up costs (why not
just set it up statically at load time?) The advantage to this sort of
approach is you can have more than one of them (eg. to be able to type
the invalid pointer.)

I believe the only sure way to get a pointer which is guaranteed to
cause a fault on a wide variety of platforms if de-referenced is going
to involve machine-dependent ifdef's in a header, it will vary from
machine to machine.

Obvious attacks are 0 on many (but not all) systems, a pointer into
text space on others (eg. the address of a function), even that
depends on certain load options being used but we'll assume that's
under control of the author, setting some magic bit (eg. pointing into
the wrong P space on a VAX), others?

In fact on some machines there may be no pointer guaranteed to produce
a fault when dereferenced (at least not without changing the operating
system which I am sure someone here will suggest as a counter-example,
wrongo, not w/in the stated constraints.) I believe an example of that
would be a PDP-11 with separate I&D space and all data space (64K)
legally allocated, NOT an unusual situation (tho PDP-11s are somewhat
unusual in this day and age.) The S/370 using 24 bit addressing may be
another example depending on the host O/S, same for a '286? (not
exactly rare and peculiar machines.) Trying to write into the space of
course makes the problem easier, but most would like an address which
faults if read (can't just destroy memory to see if a pointer is
valid.)

It's an interesting question and repeating zero adds nothing.


-- 
	-Barry Shein

Software Tool & Die, Purveyors to the Trade
1330 Beacon Street, Brookline, MA 02146, (617) 739-0202
Internet: bzs@skuld.std.com
UUCP:     encore!xylogics!skuld!bzs or uunet!skuld!bzs

gnb@bby.oz (Gregory N. Bond) (07/28/89)

In article <35077@bu-cs.BU.EDU> bzs@bu-cs.BU.EDU (Barry Shein) writes:

   Obvious attacks are 0 on many (but not all) systems, a pointer into
   text space on others (eg. the address of a function), even that
   depends on certain load options being used but we'll assume that's
   under control of the author, setting some magic bit (eg. pointing into
   the wrong P space on a VAX), others?

For word-aligned machines, (e.g. 680x0), (int *)1.  In fact, in one
application involving humungous collections of 2D linked lists (linked
plains??) I used (struct foo *)0 as uninitialised pointer, and (struct
foo *)1 as initialised-to-invalid-address pointer.  Both dumped core
when dereferenced, and help track down obscure bugs.  Especialy as I
could do (struct foo *)(2*n+1) for any n, and work out which
assignment was generating the pointer that was dereferenced.  This
method could give a very large class of distinguishable invalid
pointers, (provided they weren't (char *)'s).

Not a general solution, but neat for the problem I had.

Greg.
(No comp.lang.c in Australia.  Lucky, hey?)
--
Gregory Bond, Burdett Buckeridge & Young Ltd, Melbourne, Australia
Internet: gnb@melba.bby.oz.au    non-MX: gnb%melba.bby.oz@uunet.uu.net
Uucp: {uunet,pyramid,ubc-cs,ukc,mcvax,prlb2,nttlab...}!munnari!melba.bby.oz!gnb