[comp.unix.wizards] Referencing NULL pointers

hadley@ics.uci.edu (T.S. Hadley) (07/10/89)

   Greetings.

   By NULL pointer referencing I mean: consider the C frags:
      
      ..
      char	*s = (char *) 0, t[10];
      strcpy(t, s);
      ...
      /* or */
      ...
      TYPE	*t = (TYPE *) 0;	/* typedef struct { ... } TYPE; */
      if(t->field == 0)
      ...


   
   On my machine (sun) these statements causes segmentation violations
   during runtime.

   Apparantly, on other machines this is perfectly valid, since I see
   quite a lot of this in code created on certain non-sun machines.

   What can I do about it? Is there a flag on some C compiler to handle
   these situations? (gcc -fallow-null-pointer-referencing-idiocy :-) 

   Apologies if this subject is a dead horse.

   Grateful Thanks.
--
Tedd Hadley (hadley@ics.uci.edu)

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

In article <19367@paris.ics.uci.edu>, hadley@ics.uci.edu (T.S. Hadley) writes:
>    By NULL pointer referencing I mean: consider the C frags:
>       ...
>       TYPE	*t = (TYPE *) 0;	/* typedef struct { ... } TYPE; */
>       if(t->field == 0)
>       ...

NULL pointer dereferencing (the correct term, btw) is illegal.  That
it works on some machines is coincidental.  I regard the segmentation
fault on the Suns as a useful feature of their implementation of
the system, as it catches bugs as shown above.

guy@auspex.auspex.com (Guy Harris) (07/11/89)

>   Apparantly, on other machines this is perfectly valid, since I see
>   quite a lot of this in code created on certain non-sun machines.

No, it's not valid, it just happens to work - and only on *some* other
machines; Suns aren't the only ones that catch attempts to dereference
null pointers.

*Lots* of invalid C constructs happen to work on some implementations,
but that doesn't make them valid; it just makes you unlucky if your code
uses such a construct and you're doing your development on such an
implementation.

>   What can I do about it?

Try checking whether a pointer is null before using it, unless you know
for certain that it won't be null.  Note that the behavior when you
dereference a null pointer isn't guaranteed even if it *doesn't* drop
core - on most implementations where it doesn't, it will probably try to
look at a data item located at location 0 in your address space, and
there are *no* guarantees as to what the locations down there contain. 
In other words, the code

      TYPE	*t = (TYPE *) 0;	/* typedef struct { ... } TYPE; */
      if(t->field == 0)

may test "true" on some such implementations (i.e., "((TYPE *)0)->field"
will be zero, if the location in question at or near location 0 is
zero), and test "false" on other such implementations (i.e., if the
location in question is non-zero).

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

In article <19367@paris.ics.uci.edu> T.S. Hadley <hadley@icarus.ics.uci.edu> writes:
>   Apparantly, on other machines this is perfectly valid, since I see
>   quite a lot of this in code created on certain non-sun machines.

No, dereferencing via a null pointer is never valid in C.
It happened that one could get away with it under certain circumstances
using certain C implementations (e.g. some a.out types on 4BSD on a VAX).
That unfortunately delayed detection of such bugs until such time as the
programs were ported to systems where null pointer dereferencing was not
"benign".  By now, recent versions of UNIX should have found and fixed
the vast majority of such bugs.

john@amc-gw.UUCP (John Sambrook) (07/11/89)

As several people have commented dereferencing NULL pointers is not an 
acceptable practice, even though you can get away with it on several 
architectures.  

There is another somewhat related issue, and that is that you have to 
be careful to make sure that the type of pointer you are dereferencing is
correct for the given usage.  For example, I have seen the following code 
from time to time:

    {
    	int fd;
    	struct stat s;
    	...

    	if (read(fd, &s, sizeof(struct stat)) != sizeof(struct stat))
    	...
    }

The problem with this code is that the second argument in the read()
call is of the wrong type; it should be of type "char *", yet in fact
it is of type "struct stat *".  

I spent several years working on a machine (Data General MV series) 
where this type of code would result in a segmentation violation when
it was executed.  Needless to say, I fixed a lot of bugs like this.
Fortunately the MV had a very good compiler, which made finding bugs
like this relatively easy.





-- 
John Sambrook                             DNS: john@amc.com
Applied Microsystems Corporation	 UUCP: amc-gw!john
Redmond, Washington  98073               Dial: (206) 882-2000 ext. 630

vohra@uts.amdahl.com (Pavan Vohra) (07/11/89)

In article <1891@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:
>>   Apparantly, on other machines this is perfectly valid, since I see
>>   quite a lot of this in code created on certain non-sun machines.
>
>No, it's not valid, it just happens to work - and only on *some* other

I would say that the code is valid.

Try a rewrite: "x= ((struct somestruct *)0)->somefield".

It can be convenient to make the first page (or whatever unit)
inaccessible to cause errors for structures that are smaller
than that unit.  Maybe Sunos does this.

It looks like in the case of UTS 1.2, at least our copy of it, the
above would not cause an error, but because the default magic
has the code segment read-only, "t->somefield" would probably
cause an error provided that the offset of somefield is small.

Anyway, it is the operating system that makes the action that the
process takes valid or invalid.

KC borrowing vohra.

-- 
---
Pavan Vohra             {..hplabs|ames|ihnp4|decwrl}!amdahl!vohra
Amdahl Corporation
Sunnyvale, CA 94086-3470
---

dg@lakart.UUCP (David Goodenough) (07/11/89)

From article <19367@paris.ics.uci.edu>, by hadley@ics.uci.edu (T.S. Hadley):
>    By NULL pointer referencing I mean: consider the C frags:
>       
>       char	*s = (char *) 0, t[10];
>       strcpy(t, s);
>    
>    On my machine (sun) these statements causes segmentation violations
>    during runtime.
> 
>    Apparantly, on other machines this is perfectly valid, since I see
>    quite a lot of this in code created on certain non-sun machines.

Just remember one of the 10 commandments:

Thou shalt not follow the NULL pointer, for surely chaos and madness await
thee at it's end.

Your sun burps if you try it. Well good for it - I wish my CP/M box at home
would do so (however int i; i = *((int *) 1); does have it's uses :-) )
-- 
	dg@lakart.UUCP - David Goodenough		+---+
						IHS	| +-+-+
	....... !harvard!xait!lakart!dg			+-+-+ |
AKA:	dg%lakart.uucp@xait.xerox.com		  	  +---+

karish@forel.stanford.edu (Chuck Karish) (07/12/89)

In article <32UP02Eg3d=801@amdahl.uts.amdahl.com> vohra@amdahl.uts.amdahl.com
(KC) wrote:
>In article <1891@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:
[ It's not valid to dereference a NULL pointer ]

>Try a rewrite: "x= ((struct somestruct *)0)->somefield".

>It can be convenient to make the first page (or whatever unit)
>inaccessible to cause errors for structures that are smaller
>than that unit.  Maybe Sunos does this...

>Anyway, it is the operating system that makes the action that the
>process takes valid or invalid.

Neither the draft C standard nor Kernighan and Ritchie define the
format of an executing program.  Accessing an arbitrarily-chosen
address in memory is thus completely non-portable; there's no way to
predict whether 0 is a valid address.  The `0' value for NULL is a
token with a special meaning, not an address.

Whether non-portable usages are `valid' depends on the way the program
is to be used.  If the program is to have a long life or should be
usable on more than one architechure, don't dereference NULL pointers.

	Chuck Karish		{decwrl,hpda}!mindcrf!karish
	(415) 493-7277		karish@forel.stanford.edu

max@jma.UUCP (Max Heffler @ Landmark Graphics) (07/12/89)

In article <10515@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn) writes:
> In article <19367@paris.ics.uci.edu> T.S. Hadley <hadley@icarus.ics.uci.edu> writes:
> No, dereferencing via a null pointer is never valid in C.
> <Stuff deleted>
> "benign".  By now, recent versions of UNIX should have found and fixed
> the vast majority of such bugs.

The IBM RT PC still allows deferencing on null to be "benign".
This has caused us a fair amount of grief when porting from the RT.
-- 
Max Heffler                     uucp: ..!uunet!jma!max
Landmark Graphics Corp.         phone: (713) 579-4751
333 Cypress Run, Suite 100
Houston, Texas  77094

guy@auspex.auspex.com (Guy Harris) (07/12/89)

>I would say that the code is valid.

And you'd be wrong in saying so.  The ANSI C standard makes it quite
clear that dereferencing null pointers is a no-no, and K&R never said it
was valid.

For reasons cited in my posting, even if it *doesn't* cause a
segmentation violation, the behavior isn't guaranteed; on such systems,
you'd be most likely to get whatever stuff was lying around location 0
in your address space, and that can be almost anything. 

"valid" doesn't mean "anything you can get away with without a core
dump".

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

In article <32UP02Eg3d=801@amdahl.uts.amdahl.com> vohra@amdahl.uts.amdahl.com (KC) writes:
>Try a rewrite: "x= ((struct somestruct *)0)->somefield".

That's not valid C either.

>Anyway, it is the operating system that makes the action that the
>process takes valid or invalid.

No, there IS a C language definition or two.

croes@imec.uucp (Kris Croes) (07/12/89)

In article <19367@paris.ics.uci.edu> T.S. Hadley <hadley@icarus.ics.uci.edu> writes:
>      ..
>      char	*s = (char *) 0, t[10];
>      strcpy(t, s);
>      ...
>      /* or */
>      ...
>      TYPE	*t = (TYPE *) 0;	/* typedef struct { ... } TYPE; */
>      if(t->field == 0)
>      ...
>   On my machine (sun) these statements causes segmentation violations
>   during runtime.
As they should!!! DOMAIN and Ultrix e.g. do not give SGVIOL, and this is
a pitty because some nasty and hard to find bugs are usualy caused by 
(unknowingly) doing these things. 
>   Apparantly, on other machines this is perfectly valid, since I see
>   quite a lot of this in code created on certain non-sun machines.
VALID??? In my humble opinion this is as INVALID as it could be. NULL points to
nothing, and must not be dereferenced: consider a rather large struct xxx, then
xxx->last_field = something can scramble up your text segment,...
See quite a lot of this??? Give me ONE example of this (not written by yourself).
(PS: I recognise that in some rare cases (e.g. accessing video-memory in a PC) this
could be valid, but in any case it's no good programming practice)
>   What can I do about it? Is there a flag on some C compiler to handle
>   these situations? (gcc -fallow-null-pointer-referencing-idiocy :-) 
I hope that there is not, maybe on DOMAIN and Ultrix there should be an option
to make the program crash if NULL is dereferenced!!!. (Yes, I have a very bad
experience with this)
>   Grateful Thanks.
You're welcome.

K. Croes
-- 
--------
K. CROES - IMEC - Leuven - Belgium   ..!prlb2!imec!croes

The Demon King bites in your leg and you feel weaker.

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

[ ... stuff about dereferencing the NULL pointer deleted ...]

This discussion belongs in comp.lang.c, methinks.

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

dave@mobile.UUCP (David C. Rein) (07/13/89)

In article <32UP02Eg3d=801@amdahl.uts.amdahl.com>, vohra@uts.amdahl.com (Pavan Vohra) writes:
> In article <1891@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:
> >>   Apparantly, on other machines this is perfectly valid, since I see
> >>   quite a lot of this in code created on certain non-sun machines.
> >
> >No, it's not valid, it just happens to work - and only on *some* other
> 
> I would say that the code is valid.
> [..stuff deleted..]
> Anyway, it is the operating system that makes the action that the
> process takes valid or invalid.
> 
> KC borrowing vohra

   Well, since the operating system we are discussing here is *NIX, 
   I don't understand your comment of 'the operating system that makes the 
   action...valid or invalid', or are we just dropping the idea of
   portability?  The fact that you might be able to access 0 is meaningless.
   In AIX PS/2, if you do a (i.e.) printf("%s", (char *)0); you'll get:

   L

   since this is the magic number for AIX executables.  Basically, a program
   that makes the assumption that it can dereference possibly uninitialized
   pointers is not a well written program (in my opinion).  And, most
   important of all, since not all *NIX systems allow for this (as they should
   not!), it hinders the portability of code, which is one of the foundations of
   *NIX!
                                                        Dave Rein
----------------------------------\  /-----------------------------------------
UUCP: ..!kodak!gizzmo!lazlo!\      \/   "It just goes to show what you can do 
            mobile!dave            /\         if you're a total psychotic"
Domain: dcr0801@ritcv             /  \           -- Woody Allen
---------------------------------/    \----------------------------------------

shirono@ssd.harris.com (Roberto Shironoshita) (07/14/89)

In article <168@jma.UUCP> max@jma.UUCP (Max Heffler @ Landmark Graphics)
writes:
> The IBM RT PC still allows deferencing on null to be "benign".
> This has caused us a fair amount of grief when porting from the RT.

Jeez!  And then they say the whole world is not a vax;-)
--
______________________________________________________________________________
                               ||   Internet: shirono@ssd.harris.com
     Roberto Shironoshita      ||
      Harris Corporation       ||             ...!novavax---\
   Computer Systems Division   ||   UUCP:     ...!uunet-------!hcx1!shirono
                               ||             ...!mit-eddie-/
------------------------------------------------------------------------------
DISCLAIMER: The opinions expressed here are my own; they in no way reflect the
            opinion or policies of Harris Corporation.

rbj@dsys.ncsl.nist.gov (Root Boy Jim) (07/14/89)

? From: Guy Harris <guy@auspex.auspex.com>

? For reasons cited in my posting, even if it *doesn't* cause a
? segmentation violation, the behavior isn't guaranteed; on such systems,
? you'd be most likely to get whatever stuff was lying around location 0
? in your address space, and that can be almost anything. 

Which, under the right circumstances, might be what you want. Consider
the following structure on a 680x0:

	struct vec {
		long boot_sp;
		long boot_pc;
		long bus_trap;
		...
	} *p0 = 0;

	p0->bus_trap = (long) <some_routine>;

When probing for memory at boot time, the kernel temporarily substitutes
its own trapping routine. Admittedly, this is an uncommon use of this
technique, and it probably doesn't use a structure anyway, but it could.

? "valid" doesn't mean "anything you can get away with without a core
? dump".

This statement is absolutely correct. The case I mentioned is the only
legal use of the zero pointer. And its definitely non-portable, as no
two machines seem to have the same page zero format.

	Root Boy Jim
	Have GNU, Will Travel.

bhoward@inquiry.org (Bruce Howard) (07/14/89)

In article <168@jma.UUCP> max@jma.UUCP (Max Heffler @ Landmark Graphics) writes:
>
>The IBM RT PC still allows deferencing on null to be "benign".
>This has caused us a fair amount of grief when porting from the RT.
>-- 
>Max Heffler                     uucp: ..!uunet!jma!max
>Landmark Graphics Corp.         phone: (713) 579-4751
>333 Cypress Run, Suite 100
>Houston, Texas  77094

are you using pcc or hc?  hc in general tends to be a real
pissant about such things (it's annoying but has made me
a bit more careful)

			bruce

nagle@well.UUCP (John Nagle) (07/16/89)

     This used to be a religious argument in the LISP community, where
it takes the form "is (CAR NIL) meaningful?"  After twenty five years,
the consensus is "no".  Ten years ago, there were still opposing camps.
C is a younger language, and it may take another decade to achieve
unanimity.  But the trend is clear.  Dereferencing NULL is a Bad Thing.


					John Nagle

mmcg@bruce.OZ (Mike Mc Gaughey) (07/16/89)

vohra@uts.amdahl.com (Pavan Vohra) [11 Jul 89 07:35:39 GMT]:
> 
> I would say that the code is valid.
> 
> Try a rewrite: "x= ((struct somestruct *)0)->somefield".

Sure it's valid.  It's just that the pointer value you are using does
not point to any valid object (like a variable, or some heap space, or
writable code) and hence may not point to allocated memory.  Result:
segmentation violation.  If you want to hardwire a particular address,
that's up to you - maybe some useful information is there - but don't
bitch when it doesn't work on someone else's machine.

BTW: There _is_ a way to guarantee that you have a valid pointer -
it's called malloc :-)

--
Mike McGaughey			ACSNET:	mmcg@bruce.cs.monash.oz

"Danger, Will Robinson, cosmic storm approaching!"
	- in bruce's /vmunix.

dg@lakart.UUCP (David Goodenough) (07/17/89)

vohra@uts.amdahl.com (Pavan Vohra) sez:
> I would say that the code is valid.
> 
> Try a rewrite: "x= ((struct somestruct *)0)->somefield".

I have never been comfortable with this construct, and would submit the
following as a "safer" replacement:

	struct somestruct stuff;

	x = (char *) (&stuff.somefield) - (char *) (&stuff);

Comments?????

The above assumes that somefield is not an array: if it is you can take
your pick of:

	stuff.somefield

and:

	&stuff.somefield[0]

-- 
	dg@lakart.UUCP - David Goodenough		+---+
						IHS	| +-+-+
	....... !harvard!xait!lakart!dg			+-+-+ |
AKA:	dg%lakart.uucp@xait.xerox.com		  	  +---+

carroll@s.cs.uiuc.edu (07/17/89)

/* Written  3:44 pm  Jul 16, 1989 by jbm@eos.UUCP in s.cs.uiuc.edu:comp.unix.wizards */

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?
/* End of text from s.cs.uiuc.edu:comp.unix.wizards */
What do you mean by "INVALID"? That it will cause a core dump when
written to, just dereferenced, or is never a valid pointer value? For the
first two, no, there isn't one. For the last, you should be able to use 0
(or better, NULL from stdio.h). According to K&R(1), the value 0 for a pointer
is guaranteed not to point to a valid object. I find it difficult to believe
that ANSI-C changed that.

Alan M. Carroll                "Oh goody, the Illudium Q-36 Explosive
carroll@s.cs.uiuc.edu           Space Modulator!"
CS Grad / U of Ill @ Urbana    ...{ucbvax,pur-ee,convex}!s.cs.uiuc.edu!carroll

ned@pebbles.cad.mcc.com (CME Ned Nowotny) (07/18/89)

In article <1382@bruce.OZ> mmcg@bruce.OZ (Mike  Mc Gaughey) writes:
=>vohra@uts.amdahl.com (Pavan Vohra) [11 Jul 89 07:35:39 GMT]:
=>> 
=>> I would say that the code is valid.
=>> 
=>> Try a rewrite: "x= ((struct somestruct *)0)->somefield".
=>
=>Sure it's valid.  It's just that the pointer value you are using does
=>not point to any valid object (like a variable, or some heap space, or
=>writable code) and hence may not point to allocated memory.  Result:
=>segmentation violation.  If you want to hardwire a particular address,
=>that's up to you - maybe some useful information is there - but don't
=>bitch when it doesn't work on someone else's machine.
=>
=>BTW: There _is_ a way to guarantee that you have a valid pointer -
=>it's called malloc :-)
=>

The argument over whether or not a C compiler should allow dereferencing of
0 (NULL) or any other numeric constant purporting to be an address is unrelated
to the question of portability.  Of course, dereferencing of 0 (or any other
numeric constant) is not portable and it certainly is not a conforming ANSI-C
program.  However, there are environments where addresses can be represented
by numeric constants and there really is something important at address 0.  If
the compiler can detect dereferencing of 0, it should print a warning, but it
should not prohibit the operation.  This is even true of C compilers that
think they know the environment for which they are compiling.  After all, only
the loader really knows anything about the valid memory map.  I may use a
compiler on one machine to compile code I will use on another machine (as
long as the processors and any co-processors are compatible.)  Admittedly,
compiler venodrs can make this impossible, but it isn't required.  In summary,
there may very well be validity to dereferencing 0, but if you do it
intentionally it must be presumed that you know what you are doing.

Ned Nowotny, MCC CAD Program, Box 200195, Austin, TX  78720  Ph: (512) 338-3715
ARPA: ned@mcc.com                   UUCP: ...!cs.utexas.edu!milano!cadillac!ned
-------------------------------------------------------------------------------
"We have ways to make you scream." - Intel advertisement in the June 1989 DDJ.

chris@mimsy.UUCP (Chris Torek) (07/18/89)

In article <1759@cadillac.CAD.MCC.COM> ned@pebbles.cad.mcc.com (CME
Ned Nowotny) writes:
>The argument over whether or not a C compiler should allow dereferencing
>of 0 (NULL) or any other numeric constant purporting to be an address is
>unrelated to the question of portability.

Actually, it is related, other than in the obvious way (the result of

	*(sometype *)(some integer expression)

is never portable).  The peculiar thing is that, even on machines
where location 0 is addressible and has something there (e.g., restart
and interrupt vectors), the expression

	*(char *)0

might get you something from somewhere other than location 0.  You
may have to write

	int zero = 0;
	...
	*(char *)zero

to get at location 0.  A compiler is free to compile

	int *p = 0;
	...
	if (p != 0) { ... }
	return;

as

		link	a6,#-4
		mov.l	#0xf8000003,a6@(-4)
		...
		cmp.l	#0xf8000003,a6@(-4)
		jeq	L91
		...
	L91:	unlk	a6
		rts

Here, a nil pointer to int is being represented with the value
0xf8000003.  For debugging purposes, if nothing else, it can be handy
to have your compiler represent every different nil pointer constant as
a different bit pattern.

>"We have ways to make you scream." - Intel advertisement in the June 1989 DDJ.

(They certainly do. :-) )
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

ned@pebbles.cad.mcc.com (CME Ned Nowotny) (07/19/89)

In article <18612@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
=>In article <1759@cadillac.CAD.MCC.COM> ned@pebbles.cad.mcc.com (CME
=>Ned Nowotny) writes:
=>>The argument over whether or not a C compiler should allow dereferencing
=>>of 0 (NULL) or any other numeric constant purporting to be an address is
=>>unrelated to the question of portability.
=>
=>Actually, it is related, other than in the obvious way (the result of
=>
=>	*(sometype *)(some integer expression)
=>
=>is never portable).  The peculiar thing is that, even on machines
=>where location 0 is addressible and has something there (e.g., restart
=>and interrupt vectors), the expression
=>
=>	*(char *)0
=>
=>might get you something from somewhere other than location 0.

...A good example deleted...


Fair enough.  In fact, I would agree that it is never a good idea to
bind an object to a specific address by assigning a numeric constant
(or integer variable) to a pointer variable within a C program.

It is much better to manipulate objects through symbolic names.  Of
course, this requires a good link editor that includes the ability
to bind symbolic names to absolute addresses.  The System V link editor
did provide this capability, but it is baroque and flawed in other
ways.

Does anyone know what, if anything, UI and OSF are doing about the UNIX
link editors they will be providing?

Ned Nowotny, MCC CAD Program, Box 200195, Austin, TX  78720  Ph: (512) 338-3715
ARPA: ned@mcc.com                   UUCP: ...!cs.utexas.edu!milano!cadillac!ned
-------------------------------------------------------------------------------
"We have ways to make you scream." - Intel advertisement in the June 1989 DDJ.

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

In article <1759@cadillac.CAD.MCC.COM> ned%cad@MCC.COM (CME Ned Nowotny) writes:
>However, there are environments where addresses can be represented
>by numeric constants and there really is something important at address 0.

There better not be.  C guarantees that valid object addresses compare
unequal to null pointers, and since a null pointer constant is written
as "0" in C source code, you cannot obtain a valid object address by
casting 0 to the object pointer type.

If this is really a problem for some implementation, then it can arrange
for the "equivalent integer" mapped form of pointers to be essentially
the conventional machine address plus one, or some similar mapping that
keeps 0 from appearing to be a possible valid object address in a C
program.

webb@bass.tcspa.IBM.COM (Bill Webb) (07/22/89)

In article <168@jma.UUCP> max@jma.UUCP (Max Heffler @ Landmark Graphics) writes:
>
>The IBM RT PC still allows deferencing on null to be "benign".
>This has caused us a fair amount of grief when porting from the RT.
>-- 
>Max Heffler                     uucp: ..!uunet!jma!max
>Landmark Graphics Corp.         phone: (713) 579-4751
>333 Cypress Run, Suite 100
>Houston, Texas  77094

For record, on the RT/PC BSD 4.2 port there were two factors that
influenced the decision on what * (char *) 0 should do. One one hand we
were concerned that programs that had this sort of construct that "ran"
on the vax (that most people were using to run 4.2 BSD at that time)
would complain when the same code core dumped or failed on the RT. We
were also concerned at the time it would take to find and fix all such
code in the various utilities. On the other hand we thought that the
code with such constructs was buggy and should be fixed. In the end we
arranged that *(char *)0 would return 0 (this took a bit of effort as
execution started at zero, but it turned out that I found an
instruction that had a zero in the right place and used that).  In
retrospect I think we probably should have gone the other way on this;
or at provided a way to test code with different behaviour.

----------------------------------------------------------------
The above views are my own, not necessarily those of my employer.
Bill Webb (IBM AWD Palo Alto), (415) 855-4457).
UUCP: ...!uunet!ibmsupt!webb

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

We're using something here at Project Athena which the compiler gurus
call "Z0MAGIC" -- I don't know whether it's something they picked up
from somewhere or something they wrote themselves.  I believe we have
it installed on both our VAX and RT compilers.

What it does is, if the -Z flag is specified to the linker, cause any
executing program to unmap page zero so that dereferencing null
pointers will always cause a segfault.  It's great for debugging,
because it won't let any null pointer references slip by.  The blurb
about it from our ld(1) man page is as follows (the English seems to
be a bit scrod at the fourth line, but you get the idea):
     -Z   Similar to -z format except for two things:  (1) the
          magic number is 0420, (2) the first page of the text
          segment is filled with zeros and the entry point is
          immediately following instead of an offset of 0.  (The
          offset is 1024 on the VAX, 2048 on the RT).  The kernel
          doesn't map page 0, so a reference to NULL causes a bus
          error on the VAX and segmentation violation on IBM RT
          and SUN architectures.

I believe that the original code for this came from off of the net
somewhere, but we've done a lot of work with it to get it to work on
both the VAX and RT (it was originally only for the VAX), and to fix
some other problems.

No, I don't know where it originally came from.  And no, I don't know
if we're allowed to redistribute it, although I just asked somebody
and I may get an answer back eventually.

(Now, I've never had to use it, of course, since I never dereference
null pointers. :-)

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

chris@mimsy.UUCP (Chris Torek) (07/24/89)

In article <12928@bloom-beacon.MIT.EDU> jik@athena.mit.edu
(Jonathan I. Kamens) writes:
>"Z0MAGIC" .... I believe that the original code for this came from
>off of the net somewhere,

It did.

>but we've done a lot of work with it to get it to work on both the VAX
>and RT (it was originally only for the VAX), and to fix some other problems.

What other problems?  I tried to convince Mike to put it in 4.3BSD,
then in 4.3-tahoe, to no avail, but I have to admit I never put it
in myself.

>No, I don't know where it originally came from.  And no, I don't know
>if we're allowed to redistribute it, although I just asked somebody
>and I may get an answer back eventually.

The pack rat hits --More--

From jdb@mordor.UUCP (John Bruner) Thu Nov 29 13:14:52 1984
Relay-Version: version B 2.10.1 6/24/83; site umcp-cs.UUCP
Posting-Version: version B 2.10.2 9/5/84; site mordor.UUCP
Path: umcp-cs!seismo!ut-sally!mordor!jdb
From: jdb@mordor.UUCP (John Bruner)
Newsgroups: net.unix-wizards
Subject: Z0MAGIC: a.out without a valid page 0
Message-ID: <687@mordor.UUCP>
Date: Thu, 29-Nov-84 13:14:52 EST
Date-Received: Thu, 29-Nov-84 14:55:12 EST
Distribution: net
Organization: S-1 Project, LLNL
Lines: 637

Several months ago I posted an inquiry about UNIX systems which do
not map virtual address 0 to a valid physical address.  I was
interested in the number of library routines which would have to be
"fixed" because they depended upon being able to dereference a NULL
pointer (a zero-valued pointer on a non-tagged machine like the VAX).
The general consensus was that most library routines would work OK
without changes, especially since other implementations of UNIX on
non-DEC machines did not map 0 to a valid address.

I implemented an OPTIONAL new object format on our 4.2BSD 750's last
October.  We have been using it for locally-written programs since
then and have had no problems.  It has been helpful in finding
errors in some of our programs that were running "correctly" before.

The new object format is referred to as "-Z" format or Z0MAGIC format,
where Z0MAGIC is 0420.  A Z0MAGIC executable file differs from a
corresponding ZMAGIC executable file in that the first page of the
text segment is always filled with zeros and the start address
("a_entry" in the a.out header) is 1024 rather than zero.  The format
of the file is otherwise identical to a ZMAGIC file.  This similarity
has two advantages.  First, it is trivial to modify the debuggers and
other user-level programs to understand Z0MAGIC format, and second,
it is possible to produce a ZMAGIC binary from a Z0MAGIC binary by
simply patching the magic number.  (This may be useful for transporting
a binary from one machine to another.)

At the user level, the following things were changed:

a.out.h	new definitions added.

 libc.a	nlist.o must be recompiled (no source changes necessary)

  ld	The flag "-Z" is now recognized.  "ld" inserts 1024 bytes of
	zeros at the start of the text segment.  [Note: the default
	object format is still ZMAGIC or "-z" format.]

  cc	Because the leading 1024 bytes of zeros affect the addresses
	assigned to symbols in the text segments, it is necessary for
	the "-Z" flag to preceed all input file specifications in the
	"ld" argument list.  However, "cc" likes to call "ld":

	    ld -X /lib/crt0.o [ld flags] [files] [libraries] -lc

	The "cc" command was changed to recognize "-Z" and call the
	loader with "-Z" preceeding "/lib/crt0.o" in the arglist.

  file	The new magic number was recognized so that an appropriate
	identification message could be output.

  adb	The debugger was informed that Z0MAGIC is a valid a.out
	magic number.

  dbx	The debugger was informed that Z0MAGIC is a valid a.out
	magic number.  [Caveat: we do not use "dbx" here.]

  sdb	The debugger was informed that Z0MAGIC is a valid a.out
	magic number.  [We still use "sdb" locally.]

Also, several programs which use the macros in <a.out.h> should be
recompiled so that they understand Z0MAGIC files.  No sources
changes are necessary.  Here is a list of (at least most of) the
major ones:

  diff gprof make nm prof ranlib size strings strip symorder


At the kernel level, a new flag was added to the x_flag field in the
(shared) text structure: XPZIV (Page Zero InValid).  This flag is set
when the magic number is 0420 [why does the kernel source use literal
numeric constants for the magic numbers instead of #define-d names?].

xalloc() calls settprot() to make all pages in the text segment
readonly.  A second argument was added to settprot() which specifies
whether page 0 is to be made inaccessible.  (This argument is nonzero
if XPZIV is set.)  If it is nonzero, the access mode for the first
CLSIZE 512-byte pages of the text segment is set to 0 (no access to
anyone).  If the process attempts to reference any location in the
inaccessible area it will receive a SIGBUS signal.

When a traced child process is performing an I-space write on behalf
of its parent, procxmt() temporarily converts the text page to write
access, writes the word, and converts it back to readonly access.
A check was added to forbid writes to non-readable text pages (hence,
forbidding a write to an invalid page zero).

[BTW, I believe that the changes to a PDP-11 kernel would be similar,
although I no longer have access to an 11 to try them on.  The new object
format could be based upon NMAGIC with the first 64 bytes of the first
segmentation register left invalid.]

-------------------------------------------------------------------------


++ h/text.h
*** /tmp/,RCSt1002982	Fri Oct  5 17:07:20 1984
--- text.h	Thu Sep 20 10:39:07 1984
***************
*** 35,37
  #define	XLOCK	010		/* Being swapped in or out */
  #define	XWANT	020		/* Wanted for swapping */
  #define	XPAGI	040		/* Page in on demand from inode */

--- 35,38 -----
  #define	XLOCK	010		/* Being swapped in or out */
  #define	XWANT	020		/* Wanted for swapping */
  #define	XPAGI	040		/* Page in on demand from inode */
+ #define	XPZIV	0100		/* Page 0 is not valid (420-format files) */

-------------------------------------------------------------------------

++ sys/kern_exec.c
*** /tmp/,RCSt1002988	Fri Oct  5 17:07:28 1984
--- kern_exec.c	Thu Sep 20 10:51:28 1984
***************
*** 83,88
  	 *	407 = plain executable
  	 *	410 = RO text
  	 *	413 = demand paged RO text
  	 * Also an ASCII line beginning with #! is
  	 * the file name of a ``shell'' and arguments may be prepended
  	 * to the argument list if given here.

--- 83,89 -----
  	 *	407 = plain executable
  	 *	410 = RO text
  	 *	413 = demand paged RO text
+ 	 *	420 = demand paged R0 text, page 0 invalid
  	 * Also an ASCII line beginning with #! is
  	 * the file name of a ``shell'' and arguments may be prepended
  	 * to the argument list if given here.
***************
*** 112,117
  		u.u_exdata.ux_tsize = 0;
  		break;
  
  	case 0413:
  	case 0410:
  		if (u.u_exdata.ux_tsize == 0) {

--- 113,119 -----
  		u.u_exdata.ux_tsize = 0;
  		break;
  
+ 	case 0420:
  	case 0413:
  	case 0410:
  		if (u.u_exdata.ux_tsize == 0) {
***************
*** 300,306
  	int nargc, uid, gid;
  {
  	register size_t ts, ds, ss;
! 	int pagi;
  
  	if (u.u_exdata.ux_mag == 0413)
  		pagi = SPAGI;

--- 302,308 -----
  	int nargc, uid, gid;
  {
  	register size_t ts, ds, ss;
! 	int pagi, pziv;
  
  	if (u.u_exdata.ux_mag == 0420) {
  		pagi = SPAGI;
***************
*** 302,308
  	register size_t ts, ds, ss;
  	int pagi;
  
! 	if (u.u_exdata.ux_mag == 0413)
  		pagi = SPAGI;
  	else
  		pagi = 0;

--- 304,310 -----
  	register size_t ts, ds, ss;
  	int pagi, pziv;
  
! 	if (u.u_exdata.ux_mag == 0420) {
  		pagi = SPAGI;
  		pziv = XPZIV;
  	} else if (u.u_exdata.ux_mag == 0413) {
***************
*** 304,310
  
  	if (u.u_exdata.ux_mag == 0413)
  		pagi = SPAGI;
! 	else
  		pagi = 0;
  	if (u.u_exdata.ux_tsize!=0 && (ip->i_flag&ITEXT)==0 &&
  	    ip->i_count!=1) {

--- 306,316 -----
  
  	if (u.u_exdata.ux_mag == 0420) {
  		pagi = SPAGI;
! 		pziv = XPZIV;
! 	} else if (u.u_exdata.ux_mag == 0413) {
! 		pagi = SPAGI;
! 		pziv = 0;
! 	} else {
  		pagi = 0;
  		pziv = 0;
  	}
***************
*** 306,311
  		pagi = SPAGI;
  	else
  		pagi = 0;
  	if (u.u_exdata.ux_tsize!=0 && (ip->i_flag&ITEXT)==0 &&
  	    ip->i_count!=1) {
  		register struct file *fp;

--- 312,319 -----
  		pziv = 0;
  	} else {
  		pagi = 0;
+ 		pziv = 0;
+ 	}
  	if (u.u_exdata.ux_tsize!=0 && (ip->i_flag&ITEXT)==0 &&
  	    ip->i_count!=1) {
  		register struct file *fp;
***************
*** 369,375
  			(int)u.u_exdata.ux_dsize,
  			(int)(sizeof(u.u_exdata)+u.u_exdata.ux_tsize),
  			0, (int *)0);
! 	xalloc(ip, pagi);
  	if (pagi && u.u_procp->p_textp)
  		vinifod((struct fpte *)dptopte(u.u_procp, 0),
  		    PG_FTEXT, u.u_procp->p_textp->x_iptr,

--- 377,383 -----
  			(int)u.u_exdata.ux_dsize,
  			(int)(sizeof(u.u_exdata)+u.u_exdata.ux_tsize),
  			0, (int *)0);
! 	xalloc(ip, pagi, pziv);
  	if (pagi && u.u_procp->p_textp)
  		vinifod((struct fpte *)dptopte(u.u_procp, 0),
  		    PG_FTEXT, u.u_procp->p_textp->x_iptr,

-------------------------------------------------------------------------

++ sys/vm_text.c
*** /tmp/,RCSt1002994	Fri Oct  5 17:07:42 1984
--- vm_text.c	Fri Sep 21 09:01:32 1984
***************
*** 58,64
   * If it is being used, but is not currently in core,
   * a swap has to be done to get it back.
   */
! xalloc(ip, pagi)
  register struct inode *ip;
  {
  	register struct text *xp;

--- 58,64 -----
   * If it is being used, but is not currently in core,
   * a swap has to be done to get it back.
   */
! xalloc(ip, pagi, pziv)
  register struct inode *ip;
  {
  	register struct text *xp;
***************
*** 94,100
  		return;
  	}
  	xp->x_flag = XLOAD|XLOCK;
! 	if (pagi)
  		xp->x_flag |= XPAGI;
  	ts = clrnd(btoc(u.u_exdata.ux_tsize));
  	xp->x_size = ts;

--- 94,100 -----
  		return;
  	}
  	xp->x_flag = XLOAD|XLOCK;
! 	if (pagi) {
  		xp->x_flag |= XPAGI;
  		if (pziv)
  			xp->x_flag |= XPZIV;
***************
*** 96,101
  	xp->x_flag = XLOAD|XLOCK;
  	if (pagi)
  		xp->x_flag |= XPAGI;
  	ts = clrnd(btoc(u.u_exdata.ux_tsize));
  	xp->x_size = ts;
  	if (vsxalloc(xp) == NULL) {

--- 96,104 -----
  	xp->x_flag = XLOAD|XLOCK;
  	if (pagi) {
  		xp->x_flag |= XPAGI;
+ 		if (pziv)
+ 			xp->x_flag |= XPZIV;
+ 	}
  	ts = clrnd(btoc(u.u_exdata.ux_tsize));
  	xp->x_size = ts;
  	if (vsxalloc(xp) == NULL) {
***************
*** 111,117
  	u.u_procp->p_textp = xp;
  	xlink(u.u_procp);
  	if (pagi == 0) {
! 		settprot(RW);
  		u.u_procp->p_flag |= SKEEP;
  		(void) rdwri(UIO_READ, ip,
  			(caddr_t)ctob(tptov(u.u_procp, 0)),

--- 114,120 -----
  	u.u_procp->p_textp = xp;
  	xlink(u.u_procp);
  	if (pagi == 0) {
! 		settprot(RW, 0);
  		u.u_procp->p_flag |= SKEEP;
  		(void) rdwri(UIO_READ, ip,
  			(caddr_t)ctob(tptov(u.u_procp, 0)),
***************
*** 119,125
  			2, (int *)0);
  		u.u_procp->p_flag &= ~SKEEP;
  	}
! 	settprot(RO);
  	u.u_segflg = 0;
  	xp->x_flag |= XWRIT;
  	xp->x_flag &= ~XLOAD;

--- 122,128 -----
  			2, (int *)0);
  		u.u_procp->p_flag &= ~SKEEP;
  	}
! 	settprot(RO, pziv);
  	u.u_segflg = 0;
  	xp->x_flag |= XWRIT;
  	xp->x_flag &= ~XLOAD;

-------------------------------------------------------------------------

++ sys/sys_process.c
*** /tmp/,RCSt1003000	Fri Oct  5 17:07:51 1984
--- sys_process.c	Fri Sep 21 09:22:04 1984
***************
*** 139,144
  				goto error;
  			xp->x_iptr->i_flag &= ~ITEXT;
  		}
  		i = -1;
  		if ((i = suiword((caddr_t)ipc.ip_addr, ipc.ip_data)) < 0) {
  			if (chgprot((caddr_t)ipc.ip_addr, RW) &&

--- 139,150 -----
  				goto error;
  			xp->x_iptr->i_flag &= ~ITEXT;
  		}
+ 		/*
+ 		 * Location must be accessible for read (could be invalid
+ 		 * if in page 0 and XPZIV is set.
+ 		 */
+ 		if (!useracc((caddr_t)ipc.ip_addr, 4, B_READ))
+ 			goto error;
  		i = -1;
  		if ((i = suiword((caddr_t)ipc.ip_addr, ipc.ip_data)) < 0) {
  			if (chgprot((caddr_t)ipc.ip_addr, RW) &&

-------------------------------------------------------------------------

++ vax/vm_machdep.c
*** /tmp/,RCSt1003006	Fri Oct  5 17:07:58 1984
--- vm_machdep.c	Fri Sep 21 09:04:55 1984
***************
*** 153,159
  	return (1);
  }
  
! settprot(tprot)
  	long tprot;
  {
  	register int *ptaddr, i;

--- 153,159 -----
  	return (1);
  }
  
! settprot(tprot, pziv)
  	long tprot;
  {
  	register int *ptaddr, i;
***************
*** 161,167
  	ptaddr = (int *)mfpr(P0BR);
  	for (i = 0; i < u.u_tsize; i++) {
  		ptaddr[i] &= ~PG_PROT;
! 		ptaddr[i] |= tprot;
  	}
  	mtpr(TBIA, 0);
  }

--- 161,168 -----
  	ptaddr = (int *)mfpr(P0BR);
  	for (i = 0; i < u.u_tsize; i++) {
  		ptaddr[i] &= ~PG_PROT;
! 		if (i >= CLSIZE || !pziv)
! 			ptaddr[i] |= tprot;
  	}
  	mtpr(TBIA, 0);
  }

-------------------------------------------------------------------------


++ ld.c
*** /tmp/,RCSt1009223	Thu Nov 29 09:51:56 1984
--- /tmp/,RCSt2009223	Thu Nov 29 09:52:15 1984
***************
*** 230,235
  int	nflag;		/* pure procedure */
  int	dflag;		/* define common even with rflag */
  int	zflag;		/* demand paged  */
  long	hsize;		/* size of hole at beginning of data to be squashed */
  int	Aflag;		/* doing incremental load */
  int	Nflag;		/* want impure a.out */

--- 230,236 -----
  int	nflag;		/* pure procedure */
  int	dflag;		/* define common even with rflag */
  int	zflag;		/* demand paged  */
+ int	Zflag;		/* demand paged, page 0 not mapped (invalid) */
  long	hsize;		/* size of hole at beginning of data to be squashed */
  int	Aflag;		/* doing incremental load */
  int	Nflag;		/* want impure a.out */
***************
*** 434,440
  			continue;
  		case 'n':
  			nflag++;
! 			Nflag = zflag = 0;
  			continue;
  		case 'N':
  			Nflag++;

--- 435,441 -----
  			continue;
  		case 'n':
  			nflag++;
! 			Nflag = zflag = Zflag = 0;
  			continue;
  		case 'N':
  			Nflag++;
***************
*** 438,444
  			continue;
  		case 'N':
  			Nflag++;
! 			nflag = zflag = 0;
  			continue;
  		case 'd':
  			dflag++;

--- 439,445 -----
  			continue;
  		case 'N':
  			Nflag++;
! 			nflag = zflag = Zflag = 0;
  			continue;
  		case 'd':
  			dflag++;
***************
*** 461,466
  			goto next;
  		case 'z':
  			zflag++;
  			Nflag = nflag = 0;
  			continue;
  		default:

--- 462,477 -----
  			goto next;
  		case 'z':
  			zflag++;
+ 			Nflag = nflag = Zflag = 0;
+ 			continue;
+ 		case 'Z':
+ 			if (tsize != 0)
+ 				error(1, "-Z: too late, some text already loaded (-z assumed)");
+ 			else {
+ 				Zflag++;
+ 				tsize = pagesize;
+ 			}
+ 			zflag++;	/* -Z implies -z */
  			Nflag = nflag = 0;
  			continue;
  		default:
***************
*** 541,546
  	filname = 0;
  	middle();
  	setupout();
  	p = argv+1;
  	for (c=1; c<argc; c++) {
  		ap = *p++;

--- 552,559 -----
  	filname = 0;
  	middle();
  	setupout();
+ 	if (Zflag)
+ 		torigin += pagesize;
  	p = argv+1;
  	for (c=1; c<argc; c++) {
  		ap = *p++;
***************
*** 1019,1025
  	}
  	tout = &toutb;
  	bopen(tout, 0);
! 	filhdr.a_magic = nflag ? NMAGIC : (zflag ? ZMAGIC : OMAGIC);
  	filhdr.a_text = nflag ? tsize :
  	    round(tsize, zflag ? pagesize : sizeof (long));
  	filhdr.a_data = zflag ? round(dsize, pagesize) : dsize;

--- 1032,1038 -----
  	}
  	tout = &toutb;
  	bopen(tout, 0);
! 	filhdr.a_magic = nflag ? NMAGIC : (zflag ? (Zflag ? Z0MAGIC : ZMAGIC) : OMAGIC);
  	filhdr.a_text = nflag ? tsize :
  	    round(tsize, zflag ? pagesize : sizeof (long));
  	filhdr.a_data = zflag ? round(dsize, pagesize) : dsize;
***************
*** 1036,1042
  		else
  			filhdr.a_entry = entrypt->n_value;
  	} else
! 		filhdr.a_entry = 0;
  	filhdr.a_trsize = (rflag ? trsize:0);
  	filhdr.a_drsize = (rflag ? drsize:0);
  	bwrite((char *)&filhdr, sizeof (filhdr), tout);

--- 1049,1055 -----
  		else
  			filhdr.a_entry = entrypt->n_value;
  	} else
! 		filhdr.a_entry = Zflag ? pagesize : 0;
  	filhdr.a_trsize = (rflag ? trsize:0);
  	filhdr.a_drsize = (rflag ? drsize:0);
  	bwrite((char *)&filhdr, sizeof (filhdr), tout);
***************
*** 1043,1049
  	if (zflag) {
  		bflush1(tout);
  		biobufs = 0;
! 		bopen(tout, pagesize);
  	}
  	wroff = N_TXTOFF(filhdr) + filhdr.a_text;
  	outb(&dout, filhdr.a_data);

--- 1056,1062 -----
  	if (zflag) {
  		bflush1(tout);
  		biobufs = 0;
! 		bopen(tout, Zflag ? (2*pagesize) : pagesize);
  	}
  	wroff = N_TXTOFF(filhdr) + filhdr.a_text;
  	outb(&dout, filhdr.a_data);

-------------------------------------------------------------------------


++ cc.c
*** /tmp/,RCSt1009253	Thu Nov 29 09:53:59 1984
--- /tmp/,RCSt2009253	Thu Nov 29 09:54:01 1984
***************
*** 22,28
  int	idexit();
  char	**av, **clist, **llist, **plist;
  int	cflag, eflag, oflag, pflag, sflag, wflag, Rflag, exflag, proflag;
! int	gflag, Gflag;
  char	*dflag;
  int	exfail;
  char	*chpass;

--- 22,28 -----
  int	idexit();
  char	**av, **clist, **llist, **plist;
  int	cflag, eflag, oflag, pflag, sflag, wflag, Rflag, exflag, proflag;
! int	gflag, Gflag, Zflag;
  char	*dflag;
  int	exfail;
  char	*chpass;
***************
*** 120,125
  		case 'd':
  			dflag = argv[i];
  			continue;
  		}
  		t = argv[i];
  		c = getsuf(t);

--- 120,128 -----
  		case 'd':
  			dflag = argv[i];
  			continue;
+ 		case 'Z':
+ 			Zflag++;
+ 			continue;
  		}
  		t = argv[i];
  		c = getsuf(t);
***************
*** 244,250
  nocom:
  	if (cflag==0 && nl!=0) {
  		i = 0;
! 		av[0] = "ld"; av[1] = "-X"; av[2] = crt0; na = 3;
  		if (outfile) {
  			av[na++] = "-o";
  			av[na++] = outfile;

--- 247,256 -----
  nocom:
  	if (cflag==0 && nl!=0) {
  		i = 0;
! 		av[0] = "ld"; av[1] = "-X"; na = 2;
! 		if (Zflag)
! 			av[na++] = "-Z";
! 		av[na++] = crt0;
  		if (outfile) {
  			av[na++] = "-o";
  			av[na++] = outfile;

-- 
  John Bruner (S-1 Project, Lawrence Livermore National Laboratory)
  MILNET: jdb@mordor.ARPA [jdb@s1-c]	(415) 422-0758
  UUCP: ...!ucbvax!dual!mordor!jdb 	...!decvax!decwrl!mordor!jdb


-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

guy@auspex.auspex.com (Guy Harris) (07/25/89)

>In retrospect I think we probably should have gone the other way on this;
>or at provided a way to test code with different behaviour.

Yes.  The paged versions of System V from AT&T - the VAX one for sure,
and probably the 3B2 one as well, at least - have a "-z" flag that tells
the linker to create an image with no page zero.  Unfortunately, it's
not the default, which means there's still a fair bit of sloppy code in
S5R3, but at least you do have the choice of enforcing correct behavior.

clewis@eci386.uucp (Chris Lewis) (07/25/89)

In article <12928@bloom-beacon.MIT.EDU> jik@athena.mit.edu (Jonathan I. Kamens) writes:
>We're using something here at Project Athena which the compiler gurus
>call "Z0MAGIC" 
>
>What it does is, if the -Z flag is specified to the linker, cause any
>executing program to unmap page zero so that dereferencing null
>pointers will always cause a segfault.

Many moons ago, (Hi Dennis!), one of the development groups I worked
with wrote a sed script to parse assembler output from the C compiler, 
and when it detected indirections, inserted a sequence of instructions before
the reference to check the pointer for null and abort.  Then wrote
a shell script around it that would "cc -S", sed, and then assemble (analogous
to "cc -c" with an extra step).

This was on a VAX, running BSD4.1.  This was a pretty gruesome hack, so
gruesome that I never bothered to look at the fine details.  
They figured, so what if it ran slow - it was only used to run debug 
versions (the target trapped null dereferences by not mapping location 0).

This idea might be useful to those not having kernel source.
-- 
Chris Lewis, R.H. Lathwell & Associates: Elegant Communications Inc.
UUCP: {uunet!mnetor, utcsri!utzoo}!lsuc!eci386!clewis
Phone: (416)-595-5425