is NULL-checking redundant in accessor-functions?

B

Brian Inglis

OK, I'll buy 'formally speaking' ...


Perhaps even touching nonexistent?


Depends on what register your bitpattern resides in. Given the option
between float and int, my guess is one of the SSE registers?

The answer depends on what instruction you are to issue next, no?

The latencies though, between SSE and adress registers prohibits practical
use of SSE to calculate offsets.

(and yes, I am aware of the wonderful weirdness of Intel pointer
bitpatterns, but even in asm they are hard to actually get at)


OK ...


I wouldn't ...

You are now saying that:

free(p)
if(p)
...

... *could* result in a cast to float whith a possible raise of an
exception (NaN or whatever)

That means free() would deliberately have to set the register where it
found p to an evil pattern. And if p was on the stack (or some such),
nobody would even ever notice?

This is longwinded.


Formally ...


OK, on the Deathstation perhaps. I think you have now touched the
unimplemented part of the world, no?
;-)


I would buy this if we have had a construct like:

p = free(p);

... but this is not the case.



Ehrmm, p is still declared as a pointer and not a float. I assume my
compiler to still evaluate it as a pointer. How then can the meaning of
the (unchanged) bitpattern have changed (in an implementation other than
the imaginary evil Deathstation?)

The Deathstation could change the tag bits defining the type from
pointer to float in it's type TLB. AFAIK there's nothing in the spec
to prohibit free() from being implemented as an intrinsic which sets p
to NULL or BAD bitpattern.
 
B

Bart van Ingen Schenau

Depends on what register your bitpattern resides in. Given the option
between float and int, my guess is one of the SSE registers?

And what if the bitpattern resides in main memory?

OK ...


I wouldn't ...

You are now saying that:

free(p)
if(p)
...

... *could* result in a cast to float whith a possible raise of an
exception (NaN or whatever)

No, but it could result in a hardware exception due to loading an
invalid address.
That means free() would deliberately have to set the register where it
found p to an evil pattern. And if p was on the stack (or some such),
nobody would even ever notice?

There is no need to alter p itself. It is sufficient to mark the
address contained inh p as being invalid. (This could be done by
unmapping the segment of p from the segment table).

OK, on the Deathstation perhaps. I think you have now touched the
unimplemented part of the world, no?
;-)


I would buy this if we have had a construct like:

p = free(p);

... but this is not the case.



Ehrmm, p is still declared as a pointer and not a float. I assume my
compiler to still evaluate it as a pointer. How then can the meaning of
the (unchanged) bitpattern have changed (in an implementation other than
the imaginary evil Deathstation?)

The meaning can change because the implementation may implement a
mapping between the logical address (the value stored in p) and the
physical hardware address (the physical location of the data). This
mapping is not required to be constant, and will typically change
frequently (think about paging).
free(p) can mark the logical address as being unused, without changing
the value of p.
mvh // Jens M Andreasen

Bart v Ingen Schenau
 
D

Douglas A. Gwyn

Brian said:
The Deathstation could change the tag bits defining the type from
pointer to float in it's type TLB. AFAIK there's nothing in the spec
to prohibit free() from being implemented as an intrinsic which sets p
to NULL or BAD bitpattern.

I believe that is contrary to the general C object model,
which for objects not flagged as "volatile" does not allow
*content* (bit pattern) to be changed by external forces.
However, the *value* can change. I liked the credit card
analogy.
 
C

CBFalconer

Chris said:
..... snip ...

This issue is quite tricky, but it may become clear if you can
answer me this question: what is the value represented by the
following bit pattern?

11000011 11110101 01001000 01000000
(in hex: 0xc3 0xf5 0x48 0x40)

jmp 0x48f5; nop; (* for an 8080/z80 *) :)
 
R

Richard Bos

Jens M Andreasen said:
Depends on what register your bitpattern resides in. Given the option
between float and int, my guess is one of the SSE registers?

And if it's in memory?
I wouldn't ...

You are now saying that:

free(p)
if(p)
...

... *could* result in a cast to float whith a possible raise of an
exception (NaN or whatever)

No, he's saying that it could result in a pointer which, since the
free(), refers to an invalid page frame, being loaded in an address
register, and the processor causing a trap because of a memory
violation.
This is longwinded.

It's deadly serious. It could even be the way forward in the battle
against wild pointer errors - I would be all for it.
Ehrmm, p is still declared as a pointer and not a float. I assume my
compiler to still evaluate it as a pointer. How then can the meaning of
the (unchanged) bitpattern have changed (in an implementation other than
the imaginary evil Deathstation?)

Suppose your phone number is 098-12345. Suppose you move to a different
city, and your phone is disconnected. I dial your old number. Not a
single digit in that number has changed, yet I now get an "invalid
number" tone, rather than your voice.

Richard
 
R

Richard Bos

Jens M Andreasen said:
This is perhaps the best description of the situation so far

I would have thought that it is the following MOVE that raises the
exception. Afterall we haven't done anything nasty (yet.)

Yes, you have. You've threatened to access memory you ought to keep your
grubby paws out of, since you've given it back to the OS.
Realworld examples, please?

You miss the point. What is there to guarantee that the _next_ service
pack, bugfix, or kernel upgrade of your favourite OS doesn't implement
this feature, in the name of better safety?

Richard
 
J

Jarno A Wuolijoki

or even if every *possible* implementation does the same thing,
the behavior is still undefined as far as the standard is concerned.

So this 'thing' is simultaneously both defined and undefined?

...ah, you meant real-world-possible..
 
B

Brian Inglis

OK, on the Deathstation perhaps. I think you have now touched the
unimplemented part of the world, no?
;-)
Ehrmm, p is still declared as a pointer and not a float. I assume my
compiler to still evaluate it as a pointer. How then can the meaning of
the (unchanged) bitpattern have changed (in an implementation other than
the imaginary evil Deathstation?)

On at least one real life Deathstation, even the value returned by
malloc() could be considered "indeterminate", if virtual memory is
overcommitted: you will segfault and can be terminated.
 
B

Brian Inglis

The meaning can change because the implementation may implement a
mapping between the logical address (the value stored in p) and the
physical hardware address (the physical location of the data). This
mapping is not required to be constant, and will typically change
frequently (think about paging).
free(p) can mark the logical address as being unused, without changing
the value of p.

Some debugging implementations setup each object in its own page(s),
so that if any access is made outside allocated storage, or after
free() is called, the reference is trapped.
 
B

Brian Inglis

Yes you have: you tried to load an unmapped value
into an address register. A trap is highly appropriate.

AFAIR this would happen in 286 protected mode with invalid segment
selectors.
 
J

Jens M Andreasen

How can the usability of a credit card change when it is reported stolen?
The number on the card is unchanged.

And as long as you just keep the credit-card in your pocket, nothing bad
happens. It is when you start using the teller-machine and actually move
some values that the police will trap you. No?
Memory maps can change during the life of a process. Segments can
become valid or invalid (and having this happen during calls to malloc()
or free() is not unreasonable). Loading a "large" pointer with an
invalid segment into an i386 segment register will cause a trap. No
dereference is required (you won't get a chance to try). A NULL pointer
is treated as a special case here: you can load the pointer, but not
dereference.

I tried hard (on a pII, Chris Torek mentioned pIII) to do something wild
enough to bailout. Didn't happen. Maybe I wasn't trying hard enough?

So if you can show a shortish scary example, it would be most welcome.


When you say "large" pointer, is that a reference to the
"small/large/huge" memorymodels? Deliberately mixing the different memory
models indeed have implications.

I suppose it is not that important anymore so, never mind ...
 
D

Douglas A. Gwyn

Brian said:
On at least one real life Deathstation, even the value returned by
malloc() could be considered "indeterminate", if virtual memory is
overcommitted: you will segfault and can be terminated.

Yeah, I was appalled when I heard that Linux had done that.
That is *not* a conforming C implementation. If malloc
returns a non-null pointer, then the storage shall have
been *allocated*, i.e. it exists, has addresses, etc.
 
K

Keith Thompson

Brian Inglis said:
On at least one real life Deathstation, even the value returned by
malloc() could be considered "indeterminate", if virtual memory is
overcommitted: you will segfault and can be terminated.

I don't think so. Though it's maximally perverse, the Deathstation's
C implementation is conforming.

There are real-world (not-quite-)C implementations that behave as
described, but the Deathstation would never do such a thing.
 
K

Keith Thompson

Brian Inglis said:
Some debugging implementations setup each object in its own page(s),
so that if any access is made outside allocated storage, or after
free() is called, the reference is trapped.

Does that catch invalid references to p, or only references to *p?
 
R

Richard Bos

Brian Inglis said:
On at least one real life Deathstation, even the value returned by
malloc() could be considered "indeterminate", if virtual memory is
overcommitted: you will segfault and can be terminated.

But AFAIK that is _not_ conforming. If you get a pointer from malloc(),
you own that memory. If you pass a pointer to free(), you do not own
that memory any longer, and this is what allows the segfault.

Richard
 
F

Francis Glassborow

Douglas A. Gwyn said:
I believe that is contrary to the general C object model,
which for objects not flagged as "volatile" does not allow
*content* (bit pattern) to be changed by external forces.
However, the *value* can change. I liked the credit card
analogy.


I think you are mistaken, at least the above does not seem to match the
decision made in Sydney wrt indeterminate values. IIRC it was the almost
unanimous opinion of those present at that meeting that a change to a
bit pattern that did not change the value (for example, normalising a
pointer representation on a machine using multiple representations of
addresses) was allowed at almost any time. It was also the opinion of
those present that an indeterminate value can be represented by any and
all possible bit patterns.
 
R

Richard Bos

Jarno A Wuolijoki said:
So this 'thing' is simultaneously both defined and undefined?

..ah, you meant real-world-possible..

Even when a certain case of UB is now real-world impossible (and in the
case under discussion, it is not even that), computer science, for both
hardware and software, advances so rapidly that there's no guarantee
that it won't be possible next year, and state of the art in five.

Richard
 
G

Gordon Burditt

How can the usability of a credit card change when it is reported stolen?
And as long as you just keep the credit-card in your pocket, nothing bad
happens. It is when you start using the teller-machine and actually move
some values that the police will trap you. No?

Unless you get caught for some other reason, and the police inventory your
pockets when you are put in jail.
I tried hard (on a pII, Chris Torek mentioned pIII) to do something wild
enough to bailout. Didn't happen. Maybe I wasn't trying hard enough?

Are you using the large memory model, or it's 32-bit equivalent
(sometimes called "intergalactic model"?) The time is fast arriving
when software won't be able to tolerate the restrictive limit of a
tiny 4GB address space of a user program (for that matter, some
programs would really like to have single arrays larger than 4GB).
Then, they'll have to start using 48-bit pointers on the i386
architecture (or abandon i386 entirely, which won't happen easily),
and THAT's when you'll run into the problem that loading an invalid
address causes a trap.

Some GUI programs may have trouble fitting all the fonts into 4GB,
and I expect that an animated screen image (HDTV resolution, 64-bit
color, etc.) could get very large.
When you say "large" pointer, is that a reference to the
"small/large/huge" memorymodels? Deliberately mixing the different
memory >models indeed have implications.

Nobody said anything about "mixing" models. Any model in which a
pointer contains a protected-mode selector piece (16 bits) in a
segment register will be affected.
I suppose it is not that important anymore so, never mind ...

It will become very important in the future, when 4GB starts being
a tight constraint on a program. Whether or not it is still called
"large model" (like the DOS 16-bit segment/16-bit offset) or something
else is a different question.

Gordon L. Burditt
 
N

Nils Weller

Yeah, I was appalled when I heard that Linux had done that.

Then you may also be appalled to hear that it's not just Linux that
supports memory overcommitment; HP-UX, Tru64 UNIX, AIX, IRIX and FreeBSD
do so too, though I think Solaris and UnixWare do not.

While the usefulness of this policy is debatable for malloc() and
friends (indeed, some systems do not support lazy allocation through
the interface(s) underlying malloc()), it makes a lot of sense for
copy-on-write mappings obtained via fork() or mmap().

Consider processes occupying a large amount of virtual memory. Those
processes would not be able to create a new process and execute another
application via fork() + exec*() if the system eagerly allocates memory.
Consider shared libraries, the data segments of which need to be mapped
copy-on-write as well. Not every process linking a large shared library,
such as libc on Unix, will want to use all of it. Consider sparse arrays
and regular files mapped with mmap() and MAP_PRIVATE ...

Even if there is no way to convince you that lazy allocation does make
sense, you have to understand that it is a feature, not a bug. Linux and
the other systems I have mentioned above make it *optional*. You can
choose to use safe eager allocation instead if you so desire, using
/proc/sys/vm/overcommit_memory or sysctl vm.overcommit_memory on Linux.
The extent to which the system shall overcommit can be configured too,
using sysctl vm.overcommit_ratio.
That is *not* a conforming C implementation. If malloc
returns a non-null pointer, then the storage shall have
been *allocated*, i.e. it exists, has addresses, etc.

Then you will also have to complain about every other system application
that may disrupt the execution of a conforming C program, such as the
Unix kill command and debuggers (using ptrace() and/or procfs to alter
the state of the program.)

As long as these things are useful, people will continue to prefer their
availability in favor of strict standards compliance.
 
J

Jens M Andreasen

You miss the point ...


No I did not! You have missed my point (?)


Give me one intstance where 'p == NULL' will fail (on that line) and I
will shut up! This is after p=malloc(somesize),free(p) ..


mvh // Jens M Andreasen
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
473,772
Messages
2,569,593
Members
45,112
Latest member
VinayKumar Nevatia
Top