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