Index Home About Blog
From: torek@elf.bsdi.com (Chris Torek)
Newsgroups: comp.std.c
Subject: Re: void foo(const int *p)
Date: 25 Sep 1997 23:44:01 -0700

I think a lot of this is bogging down in a couple of different areas;
people are arguing past each other over:

 - What does const mean?
 - What could const mean?
 - What should const mean?

Many people will produce different answers for these three.

In fact, the answer to the first, in terms of the Standard, is
`almost nothing at all'.  For instance:

	const int c = 3;
	...
	printf("%d\n", c);

could print 42, if c somehow changes despite its being declared
const.  No strictly conforming program can change `c' directly,
but the object is usually subject to change through external factors
(e.g., direct writes on systems that lack memory protection, or
memory corruption from alpha particles, or whatnot).  All the
Standard gets you is these two things:

	- An object that is defined with `const' may not be modified
	  in any way by a strictly conforming program (so since `c'
	  above is const, it may not be modified; if it *is* modified,
	  the behavior is undefined).

	- An lvalue with the `const'-qualifier may not be assigned-to.
	  This mainly buys you some compile-time typo/braino checking,
	  e.g., a diagnostic if you write `c = 7' instead of `v = 7'
	  (note that c and v are next to each other on many keyboards).

The answer to the second (`what could const mean') is quite open
ended: there are plenty of possible meanings.

The answer to the third, `what should const mean', depends on the
person asked.

Note also that the following strictly conformant program *must* print
"3, 7":

	#include <stdio.h>

	int	v;
	int	*p;

	void	f(const int *);

	int main(void) {
		p = &v;
		f(&v);
		return 0;
	}

	void f(const int *vp) {
		int i, j;
		i = *vp;
		*p = 7;
		j = *vp;
		printf("%d, %d\n", i, j);
	}

The compiler *cannot* assume that i and j are equal, despite the
fact that *vp is const-qualified, because vp can (and does) point
to a modifiable lvalue and the assignment to *p can (and does)
modify the lvalue to which the const-qualified `vp' pointer points.
As this example illustrates, `const' does *not* mean `constant'.
--
In-Real-Life: Chris Torek, Berkeley Software Design Inc
El Cerrito, CA	Domain:	torek@bsdi.com	+1 510 234 3167
Antispam notice: unsolicited commercial email will be handled at my
consulting rate; pyramid-scheme mail will be forwarded to the FTC.



From: Linus Torvalds <torvalds@linux-foundation.org>
Newsgroups: fa.linux.kernel
Subject: Re: Why is the kfree() argument const?
Date: Wed, 16 Jan 2008 18:39:48 UTC
Message-ID: <fa.V9M+5l8C/um5KEiBtZOjbJDQmu4@ifi.uio.no>

On Wed, 16 Jan 2008, Johannes Weiner wrote:
>
> is there any reason why kfree() takes a const pointer just to degrade it
> with the call to slab_free()/__cache_free() again?  The promise that the
> pointee is not modified is just bogus in this case, anyway, isn't it?

"const" has *never* been about the thing not being modified. Forget all
that claptrap. C does not have such a notion.

"const" is a pointer type issue, and is meant to make certain mis-uses
more visible at compile time. It has *no* other meaning, and anybody who
thinks it has is just setting himself up for problems.

In the particular case of kfree(), the pointer argument *should* be const,
and the fact that the C library gets this wrong is not the kernels
problem, it's a problem with the C library.

Why?

 - From a very obvious and very *real* caller perspective, "free()" really
   doesn't change the thing the pointer points to. It does something
   totally different: it makes the *pointer* itself invalid.

   In other words, if you think that "kfree()" changed the thing you
   free'd, you're simply wrong. It did no such thing. The memory is 100%
   the same, it's just that you cannot access it any more, and if you try,
   you'll get somebody elses memory.

   In other words, "kfree()" can be const.

 - Anything that *can* take a const pointer should always do so.

   Why? Because we want the types to be as tight as possible, and normal
   code should need as few casts as possible.

   Here's a question for you: let's say that you have a structure that
   has a member that is never changed. To make that obvious, and to allow
   the compiler to warn about mis-use of a pointer, the structure should
   look something like

		struct mystruct {
			const char *name;
			..

   and let's look at what happens if the allocation of that const thing is
   dynamic.

   The *correct* way to do that is:

		char *name = kmalloc(...)
		/* Fill it in */
		snprintf(name, ...)
		mystruct->name = name;

   and there are no casts anywhere, and you get exactly the semantics you
   want: "name" itself isn't constant (it's obviously modified), but at
   the same time the type system makes it very clear that trying to change
   it through that mystruct member pointer is wrong.

   How do you free it?

   That's right, you do:

		kfree(mystruct->name);

   and this is why "kfree()" should take a const pointer. If it doesn't,
   you have to add an *incorrect* and totally useless cast to code that
   was correct.

So never believe that "const" is some guarantee that the memory under the
pointer doesn't change.  That is *never* true. It has never been true in
C, since there can be arbitrary pointer aliases to that memory that aren't
actually const. If you think "const *p" means that the memory behind "p"
is immutable, you're simply wrong.

Anybody who thinks that kfree() cannot (or should not) be const doesn't
understand the C type system.

			Linus


From: Linus Torvalds <torvalds@linux-foundation.org>
Newsgroups: fa.linux.kernel
Subject: Re: Why is the kfree() argument const?
Date: Wed, 16 Jan 2008 23:24:09 UTC
Message-ID: <fa.7oA549TtN1tTLMZHnDe8FehQ85U@ifi.uio.no>

On Wed, 16 Jan 2008, Christoph Lameter wrote:
>
> Correct and we have gcc 4.2 currently spitting out warnings because of
> casting to non const. Any idea how to convince gcc that this is okay?

Either don't use a broken compiler (casting a const pointer to a non-const
is definitely not a bug), or cast to "unsigned long" (if it still
complains, now the compiler is not just stupid, it's broken).

The whole point of memory management is that we know how pointers work,
and understand that they have a *bit* representation, not just the C
semantics.

		Linus


From: Linus Torvalds <torvalds@linux-foundation.org>
Newsgroups: fa.linux.kernel
Subject: Re: Why is the kfree() argument const?
Date: Wed, 16 Jan 2008 23:23:23 UTC
Message-ID: <fa.Kq7cejI9y5y9xNXQsSMZztRWlOQ@ifi.uio.no>

On Wed, 16 Jan 2008, Johannes Weiner wrote:
>
> Okay, I understood that now.  A const qualifier just forbids modifying
> the underlying memory _through this particular pointer_, right?

Yes, exactly.

> In the case of slub's kfree(), which takes a const pointer, you pass it
> on to slab_free() which actually _DOES_ modification of the underlying
> memory when linking the object to the freelist (as far as I understood
> the code).

First off, the whole concept of "underlying memory" is all about the fact
that pointers point not to separate objects with its own existence, but
point to a shared resource ("memory") that can be accessed many different
ways.

So immediately when you talk about "underlying memory", you need to
realize that now you're not talking about "that pointer" any more, but you
are talking about the stuff that *other* pointers can access.

And the whole (and *only*) point of a memory allocator is turning that
amorphous notion of "infinitely aliasable underlying memory" model into
more of a "C pointer to separate objects" model.

That *is* what a memory allocator does.

> So if I got it right and you actually modify the memory you only got a
> const pointer to

No. YOU DO NOT.

You *invalidate* the pointer. Then, the memory allocator releases that
portion of the "infinitely aliasable underlying memory", but that means
that THAT POINTER NO LONGER EXISTS ON A C LEVEL.

So no, you don't "modify the memory you only got a const pointer to".

What you do is that the memory allocator

 - keeps track of the NON-const memory that it always had
 - it sees the const pointer you gave it, and uses that pointer to look up
   ITS OWN DATA STRUCTURES.
 - it then accesses those memory locations using its own pointers.

The "const pointer" you passed to kfree() simply no longer exists. The
object it pointed to has gone away. That particular pointer simply isn't
valid any more.

But the memory management code, which maintains its own structures for
what it has allocated and not allocated, can (and will) generally use the
knowledge *it* has about its own pointers to modify its own data
structures.

The fact that they (obviously) alias in memory is irrelevant. It has no
meaning for the "const void *" you passed in. That pointer is not usable
for the caller any more.

And no, this is not just a semantic argument. It's a real issue. The
pointer you pass in to kfree() is obviously used to *find* the data
structures that the memory allocator maintains, and there is almost
inevitably a rather direct mapping between the passed-in pointers and the
data structures that a memory manager maintains itself, but they really
are different.

So to take a kfree() a pointer, the memory manager will use the "binary
value" of that pointer to find its own data structures, and sometimes the
values are so closely related that it ends up being a direct cast
(possibly with an offset), but the end result of that direct cast really
is now the memory management internal data, not the original pointer.

A real example of this is the actual kfree() implementation in the kernel
(let's take the one from SLAB):

	struct kmem_cache *c;
	..
	c = virt_to_cache(objp);

which then actually ends up doing a

	struct page *page = virt_to_head_page(obj);
	return page_get_cache(page);

which literally does math on the binary representation of the pointer to
look up the backing store values (because the memory manager by definition
is the thing that knows how the low-level representation of pointers
actually works).

So now the kernel has turned that user-supplied pointer into ITS OWN data
structures, that contain the NON-CONST aliases for the data.

(It will actually use the const pointer to further get offsets into those
things, of course, so it's several levels of indirection off that pointer)

And yes, then it will write to those non-const aliases, but that's really
no different from the same old thing: the pointer *you* passed kmalloc()
may be "const",  but the memory manager itself has its own data structures
to keep track of *its* aliases to the same memory, and those are the ones
that are actually used to re-use the memory for others (and poison it
etc).

So the particular pointer you passed in is "const". But that never *ever*
guarantees that there aren't other non-const pointers around to be used
that can modify the memory. And the memory manegement definitely has all
those aliased pointers around - it's the whole *point* of memory
management.

			Linus


From: Linus Torvalds <torvalds@linux-foundation.org>
Newsgroups: fa.linux.kernel
Subject: RE: Why is the kfree() argument const?
Date: Thu, 17 Jan 2008 21:26:28 UTC
Message-ID: <fa.Tq9S40hg1CCJqiFvVne1elg6AVA@ifi.uio.no>

On Thu, 17 Jan 2008, David Schwartz wrote:
>
> Which does change the thing the pointer points to. It goes from being a
> valid object to not existing.

No. It's the *pointer* that is no longer valid.

There's definitely a difference between "exists and is changed" and
"doesn't exist any more".

> How is ceasing to exist not a change?

It's not a change to the data behind it, it's a change to the *metadata*.
Which is something that "const" doesn't talk about at all.

> >    Why? Because we want the types to be as tight as possible, and normal
> >    code should need as few casts as possible.
>
> Right, and that's why you are wrong.

No, it's why I'm right.

"kmalloc/kfree" (or any memory manager) by definition has to play games
with pointers and do things like cast them. But the users shouldn't need
to, not for something like this.

> No, it's both correct and useful. This code is the exception to a rule. The
> rule is that the object remain unchanged and this violates that rule.

No.

You are continuing to make the mistake that you think that "const" means
that the memory behind the pointer is not going to change.

Why do you make that mistake, when it is PROVABLY NOT TRUE!

Try this trivial program:

	int main(int argc, char **argv)
	{
	        int i;
	        const int *c;

	        i = 5;
	        c = &i;
	        i = 10;
	        return *c;
	}

and realize that according to the C rules, if it returns anything but 10,
the compiler is *buggy*.

The fact is, that in spite of us having a "const int *", the data behind
that pointer may change.

So it doesn't matter ONE WHIT if you pass in a "const *" to "kfree()": it
does not guarantee that the data doesn't change, because the object you
point to has other pointers pointing to it.

This isn't worth discussing. It's really simple: a conforming program
CANNOT POSSIBLY TELL whether "kfree()" modified the data or not. As such,
AS FAR AS THE PROGRAM IS CONCERNED, kfree() takes a const pointer, and the
rule that "if it can be considered const, it should be marked const" comes
and says that kfree() should take a const pointer.

In other words - anything that could ever disagree with "const *" is BY
DEFINITION buggy.

It really is that simple.

		Linus


From: Linus Torvalds <torvalds@linux-foundation.org>
Newsgroups: fa.linux.kernel
Subject: RE: Why is the kfree() argument const?
Date: Fri, 18 Jan 2008 16:11:11 UTC
Message-ID: <fa.ZqdDhfguGP9zCcQXclJWU3IkRzI@ifi.uio.no>

On Thu, 17 Jan 2008, David Schwartz wrote:
>
> Nonsense. The 'kfree' function *destroys* the object pointer to by the
> pointer. How can you describe that as not doing anything to the object?

Here's an idea. Think it through.

Why don't we need write permissions to a file to unlink it?

Here's a hint: because unlinking doesn't *write* to it. In fact, it
doesn't read from it either. It doesn't do any access at all to that
object, it just *removes* it.

Is the file gone after you unlink it? Yes (modulo refcounting for aliasing
"pointers" aka filenames, but that's the same for any memory manager -
malloc/free just doesn't have any, so you could think of it as a
non-hardlinking filesystem).

So you're the one who are speaking nonsense. Making something "not exist"
is not at all the same thing as accessing it for a write (or a read). It
is a metadata operation that doesn't conceptually change the data in any
way, shape or form - it just makes it go away.

And btw, exactly as with kfree(), a unlink() may well do something like
"disk scrubbing" for security purposes, or cancel pending writes to the
backing store. But even though it may write (or, by undoing a pending
write, effectively "change the state") to the disk sectors that used to
contain the file data, ONLY AN IDIOT would call it "writing to the file".
Because "the file" is gone. Writing to the place where the file used to be
is a different thing.

So give it up. You're wrong. Freeing a memory area is not "writing to it"
or accessing it in *any* manner, it's an operation on another level
entirely.

			Linus

Index Home About Blog