Write only memory

J

jacob navia

In C, we have read-only memory (const), read/write memory
(normal data), and write only memory.

Let's look at the third one in more detail.

Write only memory is a piece of RAM that can only
be written to, since its contents are undefined.

The program is allocating a new piece of data, and
the previous contents aren't relevant. This memory
is generated by malloc and friends, or allocated
statically by the compiler by making the processor
increase the stack area at the entry of the function.

When you read from write only memory (you use an
uninitialized variable) the behavior is undefined,
i.e. it is declared a big mistake.

It is big because the consequences are random. When
a value is read from that memory locations, its contents
are random. We know that if we have rebooted maybe the
OS has just cleaned up and this memory is not quite
random, it is mostly zero. If the machine is running
since a while however, the contents are probably
whatever data was written to that address before, in
another program.

The symptoms are very vague. Programs that run at the
start stop working, a program that has just run crashes,
symptoms that *could* be related to this program or not.

Confusing symptoms.

This confusion is because of the random nature of the
data being introduced in the program.

The great remedy of course, is to have default values
for all local variables and set them at the start. This
doesn't eliminate all bugs, but at least
eliminates those of reading from write only memory.

As a first approach:
Data *Function(Data *d, index i)
{
Data workCopy;
DataIndex di;
etc...

memset(&workCopy,0,sizeof(Data));
memset(&di, 0, sizeof(DataIndex));
etc...
}

Shorter would be:
Data *Function(Data *d, index i)
{
Data workCopy={0};
DataIndex di={0};
etc...

}

This already much better, but it is still bothersome
if you forget one.

Even better would be if we would just write:

_Pragma(Stdc,Zeroinit,Function)

meaning that in the given function all local
data should be zeroed before use at function
entry.

But that is still too long...

Couldn't we just decide that by default all locals
are zeroed at entry of the function?

Only when you write:

_Pragma(Stdc, Nozeroinit, function)

would be the zeroing of memory be avoided.

I think that would be the best. Not to write
anything at all. This would slow software a bit,
(maybe) but for *many* applications running
in PCs today that would not do any real
performance lost.

Zero is used as default value in many situations,
pointers couldn't by chance destroy another data item
since even uninitialized pointers would be NULL.

How nice. This would mean also no change to
existing programs. They would just run a few
microseconds slower and nobody would care.

The data must be brought to the L1 cache anyway,
and zeroing locals ensures that they do not
provoke a processor L1 cache fault later within
the code of the function. A burst mode
can be probably used if present. This reduces
the cost of each cache failure: all at once.

Most locals space is small anyway.

The problems arising from reading write-only
memory would be restricted to hard traps in
the program, very easy to pinpoint to a
specific line of code.

In most implementations a NULL dereference
traps, and the error is pinpointed exactly
where it arises. With bogus values in a
pointer there is some chance that the pointer
destroys other data structures. Using NULL
there is none. The integrity of the program itself
is not destroyed.
 
C

Chris Barts

In C, we have read-only memory (const), read/write memory
(normal data), and write only memory.

Let's look at the third one in more detail.

Write only memory is a piece of RAM that can only
be written to, since its contents are undefined.

Funny... I thought write-only memory was /dev/null.

(Or the programmer after a couple beers. ;-))
The program is allocating a new piece of data, and
the previous contents aren't relevant. This memory
is generated by malloc and friends, or allocated
statically by the compiler by making the processor
increase the stack area at the entry of the function.

Or however the compiler and hardware like to do it. But this is
irrelevant.
When you read from write only memory (you use an
uninitialized variable) the behavior is undefined,
i.e. it is declared a big mistake.

It is big because the consequences are random. When
a value is read from that memory locations, its contents
are random. We know that if we have rebooted maybe the
OS has just cleaned up and this memory is not quite
random, it is mostly zero. If the machine is running
since a while however, the contents are probably
whatever data was written to that address before, in
another program.

The symptoms are very vague. Programs that run at the
start stop working, a program that has just run crashes,
symptoms that *could* be related to this program or not.

Confusing symptoms.

This confusion is because of the random nature of the
data being introduced in the program.

The great remedy of course, is to have default values
for all local variables and set them at the start. This
doesn't eliminate all bugs, but at least
eliminates those of reading from write only memory.

Yes, this is why some debuggers will initialize memory of this sort to a
known but unusual value, to turn Heisenbugs of this kind into Bohr bugs.
0xDEADBEEF and 0xCAFEBABE are common on 32-bit systems which have
debuggers with this function.

[bobbit]
This already much better, but it is still bothersome
if you forget one.

Even better would be if we would just write:

_Pragma(Stdc,Zeroinit,Function)

meaning that in the given function all local
data should be zeroed before use at function
entry.

I think all pragmas look like preprocessor directives. That is, it should
be written as:

#pragma (...)
But that is still too long...

Not for me. Not if I want my compiled code to not waste its time zeroing
RAM I'll never accidentally access, because I enforce a modicum of hygiene
on my code.
Couldn't we just decide that by default all locals
are zeroed at entry of the function?

This loses when a function needs to declare really large locals and the
programmer is smart enough to not access them before they've been written
to. Your CPU burns clock cycles and expensive RAM access time for no good
reason.
Only when you write:

_Pragma(Stdc, Nozeroinit, function)

would be the zeroing of memory be avoided.

/This/ is too much typing.
I think that would be the best. Not to write
anything at all. This would slow software a bit,
(maybe) but for *many* applications running
in PCs today that would not do any real
performance lost.

Bah. C isn't (just) for PCs. C is for embedded devices and
high-performance systems that don't want to waste time looking out for
incompetent programmers.
Zero is used as default value in many situations,
pointers couldn't by chance destroy another data item
since even uninitialized pointers would be NULL.

No. Wrong. NULL != 0x00000000 on all systems. Programmers who think it is
get bitten.
How nice. This would mean also no change to
existing programs. They would just run a few
microseconds slower and nobody would care.

Wrong. Programs controlling things like heart-lung machines and
supercomputers could run noticeably slower, and this is not always
acceptable.
The data must be brought to the L1 cache anyway,
and zeroing locals ensures that they do not
provoke a processor L1 cache fault later within
the code of the function. A burst mode
can be probably used if present. This reduces
the cost of each cache failure: all at once.

"The fool in his heart thinks `All the world's a PC.'"
Most locals space is small anyway.

Not always. It may be quite large if the function is doing lots of vector
or matrix math, as in graphical applications.
The problems arising from reading write-only
memory would be restricted to hard traps in
the program, very easy to pinpoint to a
specific line of code.

This is why you have a debugger. (This is also why you have eyes and a
brain, but apparently those cannot be safely assumed anymore.)
In most implementations a NULL dereference
traps, and the error is pinpointed exactly
where it arises. With bogus values in a
pointer there is some chance that the pointer
destroys other data structures. Using NULL
there is none. The integrity of the program itself
is not destroyed.

This loses on systems without an MMU, and an embedded system usually does
not need an MMU. Again, thinking the world is a PC is pretty stupid.
 
L

Lamb9bert

This loses when a function needs to declare really large locals and the
programmer is smart enough to not access them before they've been written
to. Your CPU burns clock cycles and expensive RAM access time for no good
reason.

Not to mention that a lot of errors come from wrong uses of malloc(). Of
course this is a lot like calloc()...
 
C

Chris Barts

Lamb9bert said:
Not to mention that a lot of errors come from wrong uses of malloc(). Of
course this is a lot like calloc()...

Hopefully, two things are true:

1. calloc() is implemented intelligently at the machine-code level, and
so can do a very fast zeroing of core because it always knows precisely
how much core it needs to wipe. A compiler, at least one that implements
VLAs, doesn't always know this.

2. The programmer doesn't use calloc() without good reason. The
programmer /especially/ doesn't use calloc() because he thinks it will
NULL an array of pointers. (This is in the FAQ, I think.)

Of course, the first one is QoI, and the second one is QoP. ;)
 
J

jacob navia

Chris said:
Yes, this is why some debuggers will initialize memory of this sort to a
known but unusual value, to turn Heisenbugs of this kind into Bohr bugs.
0xDEADBEEF and 0xCAFEBABE are common on 32-bit systems which have
debuggers with this function.

lcc-win32 uses 0xFFFA5A5A
[bobbit]

This already much better, but it is still bothersome
if you forget one.

Even better would be if we would just write:

_Pragma(Stdc,Zeroinit,Function)

meaning that in the given function all local
data should be zeroed before use at function
entry.


I think all pragmas look like preprocessor directives. That is, it should
be written as:

#pragma (...)
C99 makes _Pragma and #pragma equivalent
Not for me. Not if I want my compiled code to not waste its time zeroing
RAM I'll never accidentally access, because I enforce a modicum of hygiene
on my code.




This loses when a function needs to declare really large locals and the
programmer is smart enough to not access them before they've been written
to. Your CPU burns clock cycles and expensive RAM access time for no good
reason.




/This/ is too much typing.
This could be replaced with a compile time switch that would be global
Bah. C isn't (just) for PCs. C is for embedded devices and
high-performance systems that don't want to waste time looking out for
incompetent programmers.

Even the best programmers do make mistakes.
Of course it never happens to you...
Wrong. Programs controlling things like heart-lung machines and
supercomputers could run noticeably slower, and this is not always
acceptable.

Mmm I would prefer a heart-lung machine with no bugs
pleeeeeeeeeeeze...

This "speed at any price" attitude is widespred. Todays
embedded systems use CPUs that leave PCs behind. The
Analog Devices DSPs are 32 bit for instance, and feature
an impresive speed, very comparable to PCs.
"The fool in his heart thinks `All the world's a PC.'"

Very few modern processor do not have a cache. Yes,
I know that there are other things that PCs but I am
speaking about a PC environment.

This whole discussion brings us to several possible
modes of execution in C.

You would have a SAFE mode with many kinds of checks,
and a FAST mode, where those checks would be normally
disabled.

There is no "one fits all" solution, and splitting the
run time into several would be better.

Speed is only one of the factors in software. There are
many others like security, robustness, etc, that in
many applications are more important than "speed".
 
M

Mark F. Haigh

jacob said:
In C, we have read-only memory (const), read/write memory
(normal data), and write only memory.

Let's look at the third one in more detail.

Write only memory is a piece of RAM that can only
be written to, since its contents are undefined.

Make that 'should only be written to'. There's no reason you can't use
unsigned chars to access the location and print what you find there.
The program is allocating a new piece of data, and
the previous contents aren't relevant. This memory
is generated by malloc and friends, or allocated
statically by the compiler by making the processor
increase the stack area at the entry of the function.

When you read from write only memory (you use an
uninitialized variable) the behavior is undefined,
i.e. it is declared a big mistake.

It is big because the consequences are random. When
a value is read from that memory locations, its contents
are random.

Many compilers do a decent job at data flow analysis and catch the
obvious uses of uninitialized memory. There are both commercial and
free static data analysis tools (splint, pc-lint, etc), as well as
runtime analysis tools (valgrind, purify, etc) you can use.
Additionally, some debug runtimes initialize uninitialized memory with
sentinel values to aid debugging.
We know that if we have rebooted maybe the
OS has just cleaned up and this memory is not quite
random, it is mostly zero. If the machine is running
since a while however, the contents are probably
whatever data was written to that address before, in
another program.

The symptoms are very vague. Programs that run at the
start stop working, a program that has just run crashes,
symptoms that *could* be related to this program or not.

Not usually these days, unless you're running on an embedded system (or
a legacy OS). Because of security implications, all protected mode
operating systems I know of only supply zero-filled pages to user
processes. Usually this is done with a kernel thread that executes as
part of the idle loop.

Within a process, sure, you'll get re-used pages, but this is only
within a process' lifetime, and really has nothing to do with whether or
not the machine has been running for a while or has just been rebooted.
Confusing symptoms.

This confusion is because of the random nature of the
data being introduced in the program.

The great remedy of course, is to have default values
for all local variables and set them at the start. This
doesn't eliminate all bugs, but at least
eliminates those of reading from write only memory.

And this is certainly enforcable with static analysis tools, if that's
your cup of tea. No need to get fancy.
As a first approach:
Data *Function(Data *d, index i)
{
Data workCopy;
DataIndex di;
etc...

memset(&workCopy,0,sizeof(Data));
memset(&di, 0, sizeof(DataIndex));
etc...
}

Shorter would be:
Data *Function(Data *d, index i)
{
Data workCopy={0};
DataIndex di={0};
etc...

}

This already much better, but it is still bothersome
if you forget one.

Even better would be if we would just write:

_Pragma(Stdc,Zeroinit,Function)

I don't think it's better.
meaning that in the given function all local
data should be zeroed before use at function
entry.

But that is still too long...

Couldn't we just decide that by default all locals
are zeroed at entry of the function?

Only when you write:

_Pragma(Stdc, Nozeroinit, function)

would be the zeroing of memory be avoided.

Once again, not better.
I think that would be the best. Not to write
anything at all. This would slow software a bit,
(maybe) but for *many* applications running
in PCs today that would not do any real
performance lost.

C is mostly used where the impact would be higher than you expect. I
don't want my python and perl interpreters compiled with this. I don't
want my device drivers compiled with this.

In the embedded systems I work on, I don't want anything like this.
Especially in the more resource-constrained ones, which, believe it or
not, are becoming more and more prevalent.
Zero is used as default value in many situations,
pointers couldn't by chance destroy another data item
since even uninitialized pointers would be NULL.

How nice. This would mean also no change to
existing programs. They would just run a few
microseconds slower and nobody would care.

Unless said system does not have a MMU and you blast away the interrupt
vector table or something else important. Not to mention that NULL does
not necessarily mean 'all bits zero'.
The data must be brought to the L1 cache anyway,
and zeroing locals ensures that they do not
provoke a processor L1 cache fault later within
the code of the function. A burst mode
can be probably used if present. This reduces
the cost of each cache failure: all at once.

What? L1's come in a variety of shapes and sizes, if there's one at
all. And why pay up front for branches you might not take, for storage
you might not use. It doesn't make sense. It introduces cache pressure
and potentially causes problems where writes block reads and the write
buffer isn't deep.
Most locals space is small anyway.

Yeah, sure.
The problems arising from reading write-only
memory would be restricted to hard traps in
the program, very easy to pinpoint to a
specific line of code.

So tell me, how do you get there from here? Now you're talking about
data type trap representations. Some types are guaranteed not to have
trap representations. If I read from an uninitialized unsigned char
type, I cannot possibly cause the program to trap.
In most implementations a NULL dereference
traps, and the error is pinpointed exactly
where it arises. With bogus values in a
pointer there is some chance that the pointer
destroys other data structures. Using NULL
there is none. The integrity of the program itself
is not destroyed.

Whatever. The integrity of a program chasing around random pointers is
not likely to be saved despite your best intentions.


Mark F. Haigh
(e-mail address removed)
 
M

Mark F. Haigh

jacob navia wrote:


lcc-win32 uses 0xFFFA5A5A

That's fantastic.

This "speed at any price" attitude is widespred. Todays
embedded systems use CPUs that leave PCs behind. The
Analog Devices DSPs are 32 bit for instance, and feature
an impresive speed, very comparable to PCs.

Go do your homework, son-- you obviously don't have a clue. Or are you
one of ERT's alter-egos?



Mark F. Haigh
(e-mail address removed)
 
C

CBFalconer

jacob said:
In C, we have read-only memory (const), read/write memory
(normal data), and write only memory.

Let's look at the third one in more detail.

Write only memory is a piece of RAM that can only be written to
.... snip ...

Many moons ago, when Sylvania still made memory chips, they
produced such a device and supplied a data sheet for it. You might
find it on the web somewhere.
 
C

Chris Barts

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

jacob navia wrote:
| Chris Barts wrote:
|
|> jacob navia wrote:
|>> Even better would be if we would just write:
|>>
|>> _Pragma(Stdc,Zeroinit,Function)
|>>
|>> meaning that in the given function all local
|>> data should be zeroed before use at function
|>> entry.
|>
|>
|>
|> I think all pragmas look like preprocessor directives. That is, it should
|> be written as:
|>
|> #pragma (...)
|>
| C99 makes _Pragma and #pragma equivalent

I did not know that.

|>> Only when you write:
|>>
|>> _Pragma(Stdc, Nozeroinit, function)
|>>
|>> would be the zeroing of memory be avoided.
|>
|>
|>
|> /This/ is too much typing.
|>
| This could be replaced with a compile time switch that would be global

Then conformant programs could have wildly variant behaviors depending
on something fully outside the source code. I think a Standardized
language should minimize things like that.

|
|>
|>> I think that would be the best. Not to write
|>> anything at all. This would slow software a bit,
|>> (maybe) but for *many* applications running
|>> in PCs today that would not do any real
|>> performance lost.
|>
|>
|>
|> Bah. C isn't (just) for PCs. C is for embedded devices and
|> high-performance systems that don't want to waste time looking out for
|> incompetent programmers.
|>
|
| Even the best programmers do make mistakes.
| Of course it never happens to you...

I have never made this kind of error, no. I do, of course, make
mistakes, but I don't expect the compiler to hold my hand. I have a very
nice debugger to step through my code, and I have a nicer brain to
reason things through.

|
|>
|>> How nice. This would mean also no change to
|>> existing programs. They would just run a few
|>> microseconds slower and nobody would care.
|>
|>
|>
|> Wrong. Programs controlling things like heart-lung machines and
|> supercomputers could run noticeably slower, and this is not always
|> acceptable.
|>
|
| Mmm I would prefer a heart-lung machine with no bugs
| pleeeeeeeeeeeze...

How about a heart-lung machine that always works fast enough to keep
your heart and/or lungs going? And is still cheap enough for your
hospital to afford?

Plus, your scheme will not catch many, or even most, bugs. Programmers
writing software for essential systems should be experienced enough to
not need your proposed modifications.

|
| This "speed at any price" attitude is widespred. Todays
| embedded systems use CPUs that leave PCs behind. The
| Analog Devices DSPs are 32 bit for instance, and feature
| an impresive speed, very comparable to PCs.

Uh, what? Embedded systems are more cost-conscious than PCs, and they
are usually more heat- and power-conscious as well. (Speed usually ups
both power consumption and heat production, something that is often
unacceptable in embedded systems.)

And DSPs are something completely different.

|>
|> "The fool in his heart thinks `All the world's a PC.'"
|>
|
| Very few modern processor do not have a cache. Yes,
| I know that there are other things that PCs but I am
| speaking about a PC environment.

And here, again, you prove that you should /not/ be proposing
modifications to Standard C. C is widely used in non-PC environments,
non-workstation environments, and even non-hosted environments. /All/
modifications to Standard C must be reviewed with that firmly in mind.

[snip]

| Speed is only one of the factors in software. There are
| many others like security, robustness, etc, that in
| many applications are more important than "speed".

This is perhaps the only intelligent comment you've had so far.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.6 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFBb5uQBsEyCHtFPhsRAmegAJ9wW9Ugnq8Eb8VM9MkonRX17g2QFQCeP2Pu
2PO/fPsjuajy1VCqX9d1knA=
=3hcm
-----END PGP SIGNATURE-----
 
B

bd

jacob said:
In C, we have read-only memory (const), read/write memory
(normal data), and write only memory.

There is no write-only data in C. What would be the point?

[snip long commentary about write-only data]
The great remedy of course, is to have default values
for all local variables and set them at the start. This
doesn't eliminate all bugs, but at least
eliminates those of reading from write only memory.

Do you mean, reading from an uninitialized variable, e.g.:
void foo(void) {
int i;
printf("%d\n", i);
}

If so, you can fix this by either initializing it to something, or making
sure it's initialized somewhere before using it. Most modern compilers have
an optional setting to track reads of uninitialized variables, though it of
course cannot be totally accurate.
As a first approach:
Data *Function(Data *d, index i)
{
Data workCopy;
DataIndex di;
etc...

memset(&workCopy,0,sizeof(Data));
memset(&di, 0, sizeof(DataIndex));
etc...
}

Shorter would be:
Data *Function(Data *d, index i)
{
Data workCopy={0};
DataIndex di={0};
etc...

}

This already much better, but it is still bothersome
if you forget one.

Even better would be if we would just write:

_Pragma(Stdc,Zeroinit,Function)

meaning that in the given function all local
data should be zeroed before use at function
entry.

But that is still too long...

If you're reading an uninitialized variable, your logic is wrong. And who
says zero is a sane initialization value? For instance, with your
hypothetical _Pragma:

_Pragma(Stdc,Zeroinit,exponent)

int exponent(int base, int e) {
int v;
while(e--) {
v *= base;
}
return v;
}

As you can see, in this function (as in many others), 0 is not a sane
initializer. The correct one would be 1, but why not just specify it?
Couldn't we just decide that by default all locals
are zeroed at entry of the function?

Sure, the compiler's free to do that if it wants, as an extension. The
standard doesn't require it because it'd be a burden upon the compiler
writers, and slow down programs that don't need it.
Only when you write:

_Pragma(Stdc, Nozeroinit, function)

would be the zeroing of memory be avoided.

I think that would be the best. Not to write
anything at all. This would slow software a bit,
(maybe) but for *many* applications running
in PCs today that would not do any real
performance lost.

If the application sets the variable to a sane variable soon later, in an
often-called function, this adds up. It's not always possible to track this
with an optimizer, either:

void dostuff(void) {
SomeHandle *h;
CreateThingy(&h);
/* ... */
}

Now it's wasted a few microseconds initializing h, even though it's not
needed. If dostuff() is called frequently, this can become signifigant.
Here's a real-world example, from typical Berkeley DB usage:

void foo(void) {
DBT x;
memset(&x, 0, sizeof x);
/* do stuff with x */
}

DBT is a typedef to a structure. It's being set to zero twice here, in this
piece of code. Since this'll happen in just about every call to bdb, the
speed loss will become signifigant. And people will be using legacy
compilers for some time, so you can't just drop the memset().
Zero is used as default value in many situations,
pointers couldn't by chance destroy another data item
since even uninitialized pointers would be NULL.

How nice. This would mean also no change to
existing programs. They would just run a few
microseconds slower and nobody would care.

The data must be brought to the L1 cache anyway,
and zeroing locals ensures that they do not
provoke a processor L1 cache fault later within
the code of the function. A burst mode
can be probably used if present. This reduces
the cost of each cache failure: all at once.

Most locals space is small anyway.

The problems arising from reading write-only
memory would be restricted to hard traps in
the program, very easy to pinpoint to a
specific line of code.

In most implementations a NULL dereference
traps, and the error is pinpointed exactly
where it arises. With bogus values in a
pointer there is some chance that the pointer
destroys other data structures. Using NULL
there is none. The integrity of the program itself
is not destroyed.

And then you run your code, which is syntactically correct on some older
compiler, and wonder why it doesn't work all of a sudden. Seems like it'd
result in a lot of hard-to-track-down bugs, and slow down everything, all
for the sake of saving a bit of typing. No thanks.
 
J

Jonathan Adams

jacob navia said:
lcc-win32 uses 0xFFFA5A5A

How... easy to recognize.
C99 makes _Pragma and #pragma equivalent

Last I checked, _Pragma() *must* be of the form:

_Pragma("a single string literal")

which would be equivalent to:

#pragma a single string literal

So the syntax you are advocating above isn't valid C99. To have a form
like you are using, you would have to do a multi-level macro:

#define _MysPragma(x) _Pragma(#x)
#define _MyPragma(x,y,z) _MysPragma((x,y,z))

Then:
_MyPragma(Stdc,Zeroinit,Function)

would transform to:
_MysPragma((Stdc,Zeroinit,Function))
to:
_Pragma("(Stdc,Zeroinit,Function)")
to:
#pragma ( Stdc , Zeroinit , Function )

Whether that form is a good idea, of course, is a separate question.

Cheers,
- jonathan
 
K

Keith Thompson

jacob navia said:
In C, we have read-only memory (const), read/write memory
(normal data), and write only memory.

Let's look at the third one in more detail.

Write only memory is a piece of RAM that can only
be written to, since its contents are undefined.

I find your terminology confusing and misleading.

Read-only memory is memory that can only be read, not written. (How
and whether this is enforced is another question.)

Read/write memory is memory that can be either read or written;
presumably reading gives you the last value written to it.

In both cases, the memory remains read-only or read/write
indefinitely, regardless of what you do with it.

What you call "write only memory" is memory that *should* not be read
because it hasn't been initialized. Once you initialize it, it
becomes read/write memory. Calling it write-only implies a concept
analagous to read-only and read/write.

Just call it what it is: uninitialized memory.
 
M

Mark McIntyre

In C, we have read-only memory (const), read/write memory
(normal data), and write only memory.

The last doesn't exist.
Let's look at the third one in more detail.

Tricky, since it doesn't exist.
Write only memory is a piece of RAM that can only
be written to, since its contents are undefined.

No, thats read/write memory that you've not yet initialised. Different
animal.

Your definition of write-only is asymmetric with your definition of
read-only, which is a nono. (I'm assuming you mean constants by that. If
not, then you have no argument at all.
 
P

Peter Shaggy Haywood

Groovy hepcat jacob navia was jivin' on Thu, 14 Oct 2004 23:32:05
+0200 in comp.lang.c.
Write only memory's a cool scene! Dig it!
In C, we have read-only memory (const), read/write memory
(normal data), and write only memory.

Nonsense! In C we have const qualified objects and objects that are
not const qualified. We have objects that are modifiable and objects
(such as string literals) which, whether const qualified or not, may
not be modifiable. There is no such thing as "write only memory".
Let's look at the third one in more detail.

Write only memory is a piece of RAM that can only
be written to, since its contents are undefined.

Piffle!
The program is allocating a new piece of data, and
the previous contents aren't relevant. This memory
is generated by malloc and friends, or allocated
statically by the compiler by making the processor
increase the stack area at the entry of the function.

Complete and utter nonsense! There ain't no sech animal as a stack
in the C language. And memory allocated by malloc() can be read. It
just causes undefined behaviour if you do so before putting useful
values into it first. The same goes for any object, not just
dynamically allocated ones.
When you read from write only memory (you use an
uninitialized variable) the behavior is undefined,
i.e. it is declared a big mistake.

It is big because the consequences are random. When
a value is read from that memory locations, its contents
are random. We know that if we have rebooted maybe the
OS has just cleaned up and this memory is not quite
random, it is mostly zero. If the machine is running
since a while however, the contents are probably
whatever data was written to that address before, in
another program.

The symptoms are very vague. Programs that run at the
start stop working, a program that has just run crashes,
symptoms that *could* be related to this program or not.

Do you know of any implementation which displays such simptoms as a
result of reading an uninitialised object? The most common symptom is
spurious values being displayed by the program (based on reading
spurious values from uninitialised objects).
Confusing symptoms.

This confusion is because of the random nature of the
data being introduced in the program.

The great remedy of course, is to have default values
for all local variables and set them at the start. This

This is not always necessary. Some people like to do that, but not
everyone.
doesn't eliminate all bugs, but at least
eliminates those of reading from write only memory.

No such thing!
As a first approach:
Data *Function(Data *d, index i)
{
Data workCopy;
DataIndex di;
etc...

memset(&workCopy,0,sizeof(Data));
memset(&di, 0, sizeof(DataIndex));
etc...
}

Shorter would be:
Data *Function(Data *d, index i)
{
Data workCopy={0};
DataIndex di={0};
etc...
}

This already much better, but it is still bothersome
if you forget one.

Even better would be if we would just write:

_Pragma(Stdc,Zeroinit,Function)

meaning that in the given function all local
data should be zeroed before use at function
entry.

But that is still too long...

Couldn't we just decide that by default all locals
are zeroed at entry of the function?

No, we couldn't. The C language does not require it, and it may slow
some programs down.
Only when you write:

_Pragma(Stdc, Nozeroinit, function)

would be the zeroing of memory be avoided.

I think that would be the best. Not to write
anything at all. This would slow software a bit,
(maybe) but for *many* applications running
in PCs today that would not do any real
performance lost.

Zero is used as default value in many situations,
pointers couldn't by chance destroy another data item
since even uninitialized pointers would be NULL.

How nice. This would mean also no change to
existing programs. They would just run a few
microseconds slower and nobody would care.

The data must be brought to the L1 cache anyway,

Nonsense! The C language does not define "L1 cache".
and zeroing locals ensures that they do not
provoke a processor L1 cache fault later within
the code of the function. A burst mode

The C language does not define "burst mode".
can be probably used if present. This reduces
the cost of each cache failure: all at once.

Most locals space is small anyway.

The problems arising from reading write-only
memory would be restricted to hard traps in
the program, very easy to pinpoint to a
specific line of code.

In most implementations a NULL dereference
traps, and the error is pinpointed exactly
where it arises. With bogus values in a
pointer there is some chance that the pointer
destroys other data structures. Using NULL
there is none. The integrity of the program itself
is not destroyed.

And what is the point of all this tripe?

--

Dig the even newer still, yet more improved, sig!

http://alphalink.com.au/~phaywood/
"Ain't I'm a dog?" - Ronny Self, Ain't I'm a Dog, written by G. Sherry & W. Walker.
I know it's not "technically correct" English; but since when was rock & roll "technically correct"?
 
P

pete

jacob navia wrote:
When you read from write only memory (you use an
uninitialized variable) the behavior is undefined,
i.e. it is declared a big mistake.

The last time that I discussed this on comp.std.c,
they told me that an object with an indeterminate value,
such as a freed pointer or even an uninitialized object,
could be read as an array of unsigned char.
 
P

pete

pete said:
The last time that I discussed this on comp.std.c,
they told me that an object with an indeterminate value,
such as a freed pointer or even an uninitialized object,
could be read as an array of unsigned char.

A kind of memory that changes it's status after a write,
is not consistent with what
"read-only memory (const), read/write memory"
means.

What you've done by coining "write only" is that you have attempted
to classify memory based on whether it has an indeterminate
value according to the object's declared type, and you've got it wrong.
 
C

CBFalconer

Peter said:
Groovy hepcat jacob navia was jivin'
.... snip ...

And what is the point of all this tripe?

I originally thought he was making a joke and didn't bother reading
it. From the portion you quoted it appears he was actually
serious! He is proposing YANCIC [1] in the wrong forum.

[1] Yet another Navian change in C.
 
R

Rob Thorpe

jacob navia said:
-- snip --
Most locals space is small anyway.

The problems arising from reading write-only
memory would be restricted to hard traps in
the program, very easy to pinpoint to a
specific line of code.

Most local space is small, but do you know how many times it is
recreated. In something I wrote a tokenising function can be called
of the order of 1 billion times. Granted, this is not the best
design, but at present it works tolerably quickly. If this tiny
function also had to wipe the local memory it uses it might well make
the whole application much slower.

Bits of code that depend on recursion could also suffer.

Extensions like those you mention might be useful in this area though.
 
D

Dan Pop

In said:
In C, we have read-only memory (const), read/write memory
(normal data), and write only memory.

Let's look at the third one in more detail.

Write only memory is a piece of RAM that can only
be written to, since its contents are undefined.

But, once you have written something to it, it is no longer write only,
according to your own definition.

Real write only "memory" has the property that it cannot be read back
and produce the value that was previously written to it. The memory
mapped registers of certain I/O devices behave like this. The same
memory location can be both read and written, but with different
semantics (e.g. it is convenient to map the Tx and Rx registers of
an UART at the same address).
The program is allocating a new piece of data, and
the previous contents aren't relevant. This memory
is generated by malloc and friends, or allocated
statically by the compiler by making the processor
increase the stack area at the entry of the function.

When you read from write only memory (you use an
uninitialized variable) the behavior is undefined,
i.e. it is declared a big mistake.

It is big because the consequences are random. When
a value is read from that memory locations, its contents
are random. We know that if we have rebooted maybe the
OS has just cleaned up and this memory is not quite
random, it is mostly zero. If the machine is running
since a while however, the contents are probably
whatever data was written to that address before, in
another program.

The symptoms are very vague. Programs that run at the
start stop working, a program that has just run crashes,
symptoms that *could* be related to this program or not.

Confusing symptoms.

This confusion is because of the random nature of the
data being introduced in the program.

The great remedy of course, is to have default values
for all local variables and set them at the start. This
doesn't eliminate all bugs, but at least
eliminates those of reading from write only memory.

You are naive. Most compilers can detect most attempts to read from
uninitialised memory, when automatically allocated. Statically allocated
memory is always initialised and, for dynamically allocated memory, there
is always calloc.

The *hard* cases are detecting attempts to read values that have become
indeterminate during the program execution. Examples:

#include <stdlib.h>

int *foo(void)
{
int i;
return &i;
}

int main()
{
char *p = malloc(10);
int *q = foo();

free(p);
p[5] = 'a';
*q = 42;
}

The value of p becomes indeterminate after the free call and any attempt
to use it (except for examining its representation on a byte by byte
basis) results in undefined behaviour.

The pointer value returned by foo() is already indeterminate when
assigned to q, because the pointed-to object no longer exists. Writing
through q can have very *interesting* effects, indeed.

These *are* a source of very subtle bugs, indeed. If you want to try
your hand at doing something useful, have your compiler detect *all*
the pointers that become indeterminate when an object no longer exists
(through one or another of the two mechanisms illustrated above)
and nullify their values. This way, any attempt to read or write
through them could reliably crash the program.

Good luck,
Dan
 

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

Members online

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,121
Latest member
LowellMcGu
Top