Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #66967 > unrolled thread

Re: I want to ask a question...

Started byGareth Owen <gwowen@gmail.com>
First post2015-08-07 20:00 +0100
Last post2015-08-22 12:23 -0700
Articles 20 on this page of 34 — 12 participants

Back to article view | Back to comp.lang.c

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  Re: I want to ask a question... Gareth Owen <gwowen@gmail.com> - 2015-08-07 20:00 +0100
    Re: I want to ask a question... glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-08-07 19:04 +0000
    Re: I want to ask a question... Keith Thompson <kst-u@mib.org> - 2015-08-07 15:30 -0700
      Re: I want to ask a question... glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-08-07 22:52 +0000
        Re: I want to ask a question... Keith Thompson <kst-u@mib.org> - 2015-08-07 16:46 -0700
          Re: I want to ask a question... glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-08-07 23:29 +0000
            Re: I want to ask a question... Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-08-08 01:37 +0100
            Re: I want to ask a question... Keith Thompson <kst-u@mib.org> - 2015-08-07 18:55 -0700
              Re: I want to ask a question... glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-08-08 05:51 +0000
                Re: I want to ask a question... Robert Wessel <robertwessel2@yahoo.com> - 2015-08-08 01:44 -0500
      Re: I want to ask a question... Ken Brody <kenbrody@spamcop.net> - 2015-08-10 12:46 -0400
      Re: I want to ask a question... supercat@casperkitty.com - 2015-08-19 16:43 -0700
        Re: I want to ask a question... Keith Thompson <kst-u@mib.org> - 2015-08-19 18:09 -0700
          Re: I want to ask a question... glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-08-20 01:00 +0000
            Re: I want to ask a question... Keith Thompson <kst-u@mib.org> - 2015-08-19 20:25 -0700
              Re: I want to ask a question... supercat@casperkitty.com - 2015-08-20 08:15 -0700
                Re: I want to ask a question... Keith Thompson <kst-u@mib.org> - 2015-08-20 11:38 -0700
                  Re: I want to ask a question... glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-08-20 18:47 +0000
                  Re: I want to ask a question... supercat@casperkitty.com - 2015-08-20 14:01 -0700
              Re: I want to ask a question... glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-08-20 16:43 +0000
                Re: I want to ask a question... James Kuyper <jameskuyper@verizon.net> - 2015-08-20 13:02 -0400
                  Re: I want to ask a question... Tonton Th <tTh@nowhere.invalid> - 2015-08-20 17:40 +0000
                    Re: I want to ask a question... James Kuyper <jameskuyper@verizon.net> - 2015-08-20 15:27 -0400
                      Re: I want to ask a question... David Brown <david.brown@hesbynett.no> - 2015-08-20 22:58 +0200
                        Re: I want to ask a question... James Kuyper <jameskuyper@verizon.net> - 2015-08-20 23:24 -0400
                          Re: I want to ask a question... glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-08-21 06:19 +0000
                          Re: I want to ask a question... Joe Pfeiffer <pfeiffer@cs.nmsu.edu> - 2015-08-21 09:20 -0600
                          Re: I want to ask a question... supercat@casperkitty.com - 2015-08-22 12:25 -0700
                            Re: I want to ask a question... Robert Wessel <robertwessel2@yahoo.com> - 2015-08-23 01:33 -0500
                    Re: I want to ask a question... glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-08-20 19:28 +0000
                Re: I want to ask a question... Keith Thompson <kst-u@mib.org> - 2015-08-20 12:11 -0700
            Re: I want to ask a question... David Brown <david.brown@hesbynett.no> - 2015-08-20 10:02 +0200
          Re: I want to ask a question... raltbos@xs4all.nl (Richard Bos) - 2015-08-22 13:42 +0000
            Re: I want to ask a question... supercat@casperkitty.com - 2015-08-22 12:23 -0700

Page 1 of 2  [1] 2  Next page →


#66967 — Re: I want to ask a question...

FromGareth Owen <gwowen@gmail.com>
Date2015-08-07 20:00 +0100
SubjectRe: I want to ask a question...
Message-ID<87pp2y7wl7.fsf@gmail.com>
Ramine <ramine@1.1> writes:

> Suppose that you are using a pointer that points to an allocated chunk
> of memory, and suppose that you deallocated this chunk of memory and
> you have keeped the old address of this pointer inside another pointer
> , is there a problem to access the old address of the pointer and
> content of the memory even if it was deallocated ?

You can access the pointer itself, but you can't access the memory it
points to.  Sometimes it might work - and it simple programs it probably
will work more ofhen than not - but you can't rely on it.

Don't do that.

[toc] | [next] | [standalone]


#66969

Fromglen herrmannsfeldt <gah@ugcs.caltech.edu>
Date2015-08-07 19:04 +0000
Message-ID<mq2vgh$7i5$1@speranza.aioe.org>
In reply to#66967
Gareth Owen <gwowen@gmail.com> wrote:
> Ramine <ramine@1.1> writes:
 
>> Suppose that you are using a pointer that points to an allocated chunk
>> of memory, and suppose that you deallocated this chunk of memory and
>> you have keeped the old address of this pointer inside another pointer
>> , is there a problem to access the old address of the pointer and
>> content of the memory even if it was deallocated ?
 
> You can access the pointer itself, but you can't access the memory it
> points to.  Sometimes it might work - and it simple programs it probably
> will work more ofhen than not - but you can't rely on it.

Some systems allow it, some don't.  For the ones that do, you don't
know that something else hasn't used it in the mean time. 
 
> Don't do that.

To allow for systems that don't, C doesn't allow it.

If you do it accidentally, the system may or may not have
a trap to detect it.

-- glen

[toc] | [prev] | [next] | [standalone]


#66977

FromKeith Thompson <kst-u@mib.org>
Date2015-08-07 15:30 -0700
Message-ID<lnmvy2g291.fsf@kst-u.example.com>
In reply to#66967
Gareth Owen <gwowen@gmail.com> writes:
> Ramine <ramine@1.1> writes:
>> Suppose that you are using a pointer that points to an allocated chunk
>> of memory, and suppose that you deallocated this chunk of memory and
>> you have keeped the old address of this pointer inside another pointer
>> , is there a problem to access the old address of the pointer and
>> content of the memory even if it was deallocated ?
>
> You can access the pointer itself, but you can't access the memory it
> points to.  Sometimes it might work - and it simple programs it probably
> will work more ofhen than not - but you can't rely on it.
>
> Don't do that.

That's not quite correct.

Accessing a pointer value after the object it points to no longer exists
has undefined behavior, even if you don't dereference it.  It's not
likely to do anything nasty on most systems, but in principle an
implementation could mark the pointer value as invalid and trap on any
reference to it.

An example:

#include <stdlib.h>
#include <stdio.h>
int main(void) {
    int *p = malloc(sizeof *p);
    if (p == NULL) exit(EXIT_FAILURE);
    *p = 42;
    printf("p = %p\n", (void*)p); // OK
    printf("*p = %d\n", *p);      // OK, prints 42

    free(p);

    printf("p = %p\n", (void*)p);
        // Undefined behavior, *probably* won't crash

    printf("*p = %d\n", *p);
        // Undefined behavior, *probably* won't crash
        // The memory location may or may not have been clobbered.

    return 0;
}

Don't access either the pointer itself *or* the memory it points to.

Of course a write access to the pointer object (such as assigning a new
value to it) is fine, since it doesn't access the stored value.

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

[toc] | [prev] | [next] | [standalone]


#66978

Fromglen herrmannsfeldt <gah@ugcs.caltech.edu>
Date2015-08-07 22:52 +0000
Message-ID<mq3cqv$7el$1@speranza.aioe.org>
In reply to#66977
Keith Thompson <kst-u@mib.org> wrote:
> Gareth Owen <gwowen@gmail.com> writes:

(snip)
>> You can access the pointer itself, but you can't access the memory it
>> points to.  Sometimes it might work - and it simple programs it probably
>> will work more ofhen than not - but you can't rely on it.

>> Don't do that.
 
> That's not quite correct.
 
> Accessing a pointer value after the object it points to no longer exists
> has undefined behavior, even if you don't dereference it.  It's not
> likely to do anything nasty on most systems, but in principle an
> implementation could mark the pointer value as invalid and trap on any
> reference to it.

That is true, but even on systems where it could cause problems,
compilers try to avoid it.

For protected mode 80286, loading a segment selector register
also loads the corresponding segment descriptor. That operation
will fail if the desriptor isn't valid. (0 is a special null
segment selector that doesn't load a descriptor.)

In copying pointers, compilers will normally not load a segment
selector into a segment register, but will either use normal 
registers or other ways to move data. 

There have been systems were all memory locations included
descriptor bits. On those systems, it might not be possible to
avoid a trap copying a pointer to unallocated memory.
Those might exist in museums.

(I remember one system where the whole protection model depends
on the compiler never generating code that could do anything
it shouldn't do, and only compiler generated code can be executed.
There might not be a C compiler for it, though.)

-- glen
 

[toc] | [prev] | [next] | [standalone]


#66979

FromKeith Thompson <kst-u@mib.org>
Date2015-08-07 16:46 -0700
Message-ID<lnio8qfyr7.fsf@kst-u.example.com>
In reply to#66978
glen herrmannsfeldt <gah@ugcs.caltech.edu> writes:
> Keith Thompson <kst-u@mib.org> wrote:
>> Gareth Owen <gwowen@gmail.com> writes:
> (snip)
>>> You can access the pointer itself, but you can't access the memory it
>>> points to.  Sometimes it might work - and it simple programs it probably
>>> will work more ofhen than not - but you can't rely on it.
>
>>> Don't do that.
>  
>> That's not quite correct.
>  
>> Accessing a pointer value after the object it points to no longer exists
>> has undefined behavior, even if you don't dereference it.  It's not
>> likely to do anything nasty on most systems, but in principle an
>> implementation could mark the pointer value as invalid and trap on any
>> reference to it.
>
> That is true, but even on systems where it could cause problems,
> compilers try to avoid it.
>
> For protected mode 80286, loading a segment selector register
> also loads the corresponding segment descriptor. That operation
> will fail if the desriptor isn't valid. (0 is a special null
> segment selector that doesn't load a descriptor.)
>
> In copying pointers, compilers will normally not load a segment
> selector into a segment register, but will either use normal 
> registers or other ways to move data. 
[...]

It's also one of those cases where even if the behavior *weren't*
undefined, it would still be a good idea to avoid it.

Once you've passed a pointer to free(), there's not much reason to look
at its value again.  If your program reads a pointer value after passing
it to free(), it's most likely because of a logical error in the program
(unless you're just doing it to see what happens).

Similarly, discussions of the fact that
    i = i++;
has undefined behavior tend to overlook the fact that *whatever* it's
intended to do, there's certainly a clearer and simpler way to express
it that intent.  Even if the order of evaluation and side effects were
strictly defined, it would still be better just to write:
    i++;

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

[toc] | [prev] | [next] | [standalone]


#66980

Fromglen herrmannsfeldt <gah@ugcs.caltech.edu>
Date2015-08-07 23:29 +0000
Message-ID<mq3f0j$bgp$1@speranza.aioe.org>
In reply to#66979
Keith Thompson <kst-u@mib.org> wrote:

(snip regarding references, even without dereferencing, pointers
to unallocated memory.)
(then I wrote)

>> That is true, but even on systems where it could cause problems,
>> compilers try to avoid it.

>> For protected mode 80286, loading a segment selector register
>> also loads the corresponding segment descriptor. That operation
>> will fail if the desriptor isn't valid. (0 is a special null
>> segment selector that doesn't load a descriptor.)

>> In copying pointers, compilers will normally not load a segment
>> selector into a segment register, but will either use normal 
>> registers or other ways to move data. 
> [...]
 
> It's also one of those cases where even if the behavior *weren't*
> undefined, it would still be a good idea to avoid it.

Yes. But even so, it might surprise someone.
 
> Once you've passed a pointer to free(), there's not much reason to look
> at its value again.  If your program reads a pointer value after passing
> it to free(), it's most likely because of a logical error in the program
> (unless you're just doing it to see what happens).

OK, consider an array of pointers, some of which might not point
to anything (yet), or might point to free()'d memory. Now, consider
making a copy of that array. Even if you know that the program only
dereferences pointers to actual allocated memory, it might not be
so easy to avoid copying them.

I presume you are still not allowed to copy them with memcpy(),
which you might want to use to copy such an array. You can see
that it might be a lot easier (and faster) to memcpy() a whole 
array, instead of carefully testing each one.

But yes, it is better to do it right.
 
> Similarly, discussions of the fact that
>    i = i++;
> has undefined behavior tend to overlook the fact that *whatever* it's
> intended to do, there's certainly a clearer and simpler way to express
> it that intent.  Even if the order of evaluation and side effects were
> strictly defined, it would still be better just to write:
>    i++;

I agree.

-- glen

[toc] | [prev] | [next] | [standalone]


#66981

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2015-08-08 01:37 +0100
Message-ID<87vbcq4nuf.fsf@bsb.me.uk>
In reply to#66980
glen herrmannsfeldt <gah@ugcs.caltech.edu> writes:

> Keith Thompson <kst-u@mib.org> wrote:
>
> (snip regarding references, even without dereferencing, pointers
> to unallocated memory.)
> (then I wrote)
>
>>> That is true, but even on systems where it could cause problems,
>>> compilers try to avoid it.
>
>>> For protected mode 80286, loading a segment selector register
>>> also loads the corresponding segment descriptor. That operation
>>> will fail if the desriptor isn't valid. (0 is a special null
>>> segment selector that doesn't load a descriptor.)
>
>>> In copying pointers, compilers will normally not load a segment
>>> selector into a segment register, but will either use normal 
>>> registers or other ways to move data. 
>> [...]
>  
>> It's also one of those cases where even if the behavior *weren't*
>> undefined, it would still be a good idea to avoid it.
>
> Yes. But even so, it might surprise someone.
>  
>> Once you've passed a pointer to free(), there's not much reason to look
>> at its value again.  If your program reads a pointer value after passing
>> it to free(), it's most likely because of a logical error in the program
>> (unless you're just doing it to see what happens).
>
> OK, consider an array of pointers, some of which might not point
> to anything (yet), or might point to free()'d memory. Now, consider
> making a copy of that array. Even if you know that the program only
> dereferences pointers to actual allocated memory, it might not be
> so easy to avoid copying them.
>
> I presume you are still not allowed to copy them with memcpy(),

I can't see why.  I think you can copy the bytes of any object without a
problem.

Further, I think you could wrap the array of pointers in a struct and
copy it by assignment because a struct (or union) itself is never a trap
representation, no matter what the component objects might be.

<snip>
-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#66983

FromKeith Thompson <kst-u@mib.org>
Date2015-08-07 18:55 -0700
Message-ID<lnegjefsrd.fsf@kst-u.example.com>
In reply to#66980
glen herrmannsfeldt <gah@ugcs.caltech.edu> writes:
> Keith Thompson <kst-u@mib.org> wrote:
[...]
>> Once you've passed a pointer to free(), there's not much reason to look
>> at its value again.  If your program reads a pointer value after passing
>> it to free(), it's most likely because of a logical error in the program
>> (unless you're just doing it to see what happens).
>
> OK, consider an array of pointers, some of which might not point
> to anything (yet), or might point to free()'d memory. Now, consider
> making a copy of that array. Even if you know that the program only
> dereferences pointers to actual allocated memory, it might not be
> so easy to avoid copying them.
>
> I presume you are still not allowed to copy them with memcpy(),
> which you might want to use to copy such an array. You can see
> that it might be a lot easier (and faster) to memcpy() a whole 
> array, instead of carefully testing each one.

Yes, you can use mempcy().  memcpy() operates (logically) on character
values, which have no trap representations.

Also, the value of a structure or union object is never a trap
representation, even if some of its members might be (N1570 6.2.6p6).

[...]

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

[toc] | [prev] | [next] | [standalone]


#66989

Fromglen herrmannsfeldt <gah@ugcs.caltech.edu>
Date2015-08-08 05:51 +0000
Message-ID<mq45cr$hao$1@speranza.aioe.org>
In reply to#66983
Keith Thompson <kst-u@mib.org> wrote:

(snip on referencing pointers to unallocated memory)
(then I wrote)
>> I presume you are still not allowed to copy them with memcpy(),
>> which you might want to use to copy such an array. You can see
>> that it might be a lot easier (and faster) to memcpy() a whole 
>> array, instead of carefully testing each one.
 
> Yes, you can use mempcy().  memcpy() operates (logically) on character
> values, which have no trap representations.

Interesting loophole in the pointer reference question.
 
> Also, the value of a structure or union object is never a trap
> representation, even if some of its members might be (N1570 6.2.6p6).

I suppose there aren't so many hardware processors that don't allow
such copying of pointers now, but some virtual machines don't like it.

Then do I suppose you can also memcmp() pointers that you couldn't
otherwise compare?

-- glen

[toc] | [prev] | [next] | [standalone]


#66992

FromRobert Wessel <robertwessel2@yahoo.com>
Date2015-08-08 01:44 -0500
Message-ID<pu8bsapvjat7pjtfoer62sfe4vcsahodbb@4ax.com>
In reply to#66989
On Sat, 8 Aug 2015 05:51:23 +0000 (UTC), glen herrmannsfeldt
<gah@ugcs.caltech.edu> wrote:

>Keith Thompson <kst-u@mib.org> wrote:
>
>(snip on referencing pointers to unallocated memory)
>(then I wrote)
>>> I presume you are still not allowed to copy them with memcpy(),
>>> which you might want to use to copy such an array. You can see
>>> that it might be a lot easier (and faster) to memcpy() a whole 
>>> array, instead of carefully testing each one.
> 
>> Yes, you can use mempcy().  memcpy() operates (logically) on character
>> values, which have no trap representations.
>
>Interesting loophole in the pointer reference question.
> 
>> Also, the value of a structure or union object is never a trap
>> representation, even if some of its members might be (N1570 6.2.6p6).
>
>I suppose there aren't so many hardware processors that don't allow
>such copying of pointers now, but some virtual machines don't like it.
>
>Then do I suppose you can also memcmp() pointers that you couldn't
>otherwise compare?


Sure, but the results of a memcmp() are not meaningful (in the sense
of what's required by the standard) in terms of what a comparison of
two pointers would yield.  For example, on typical little endian
platforms the result of the memcmp() of two pointers will not relate
to the ordering of a proper comparison of those pointers (since the
memcmp() will be doing a big-endian comparison).  Not even equality
testing is required to produce the same results.

[toc] | [prev] | [next] | [standalone]


#67075

FromKen Brody <kenbrody@spamcop.net>
Date2015-08-10 12:46 -0400
Message-ID<mqakda$srg$1@dont-email.me>
In reply to#66977
On 8/7/2015 6:30 PM, Keith Thompson wrote:
> Gareth Owen <gwowen@gmail.com> writes:
>> Ramine <ramine@1.1> writes:
>>> Suppose that you are using a pointer that points to an allocated chunk
>>> of memory, and suppose that you deallocated this chunk of memory and
>>> you have keeped the old address of this pointer inside another pointer
>>> , is there a problem to access the old address of the pointer and
>>> content of the memory even if it was deallocated ?
>>
>> You can access the pointer itself, but you can't access the memory it
>> points to.  Sometimes it might work - and it simple programs it probably
>> will work more ofhen than not - but you can't rely on it.
>>
>> Don't do that.
>
> That's not quite correct.
>
> Accessing a pointer value after the object it points to no longer exists
> has undefined behavior, even if you don't dereference it.  It's not
> likely to do anything nasty on most systems, but in principle an
> implementation could mark the pointer value as invalid and trap on any
> reference to it.
[...]

In old Intel "386 protected mode", where memory is accessed via a 
selector+offset, merely loading a selector register with an invalid value 
would trigger a fault.  In an implementation where "far" allocated memory 
would be given its own selector, free()ing that memory would make the 
selector invalid.  Unless a subsequent allocation gave the same selector, 
simply accessing the pointer, even without dereferencing it, could trigger 
that fault.

(Actually, I think that may still be true, but given today's 32- and 64-bit 
offsets, the notion of "far pointers" is usually irrelevant.)

-- 
Kenneth Brody

[toc] | [prev] | [next] | [standalone]


#67860

Fromsupercat@casperkitty.com
Date2015-08-19 16:43 -0700
Message-ID<d9ea783a-d784-4e13-8133-3cb8d7b42032@googlegroups.com>
In reply to#66977
On Friday, August 7, 2015 at 4:48:18 PM UTC-5, Keith Thompson wrote:
> Accessing a pointer value after the object it points to no longer exists
> has undefined behavior, even if you don't dereference it.  It's not
> likely to do anything nasty on most systems, but in principle an
> implementation could mark the pointer value as invalid and trap on any
> reference to it.

If a pointer isn't null, doesn't identify a valid object, and isn't a
"one-past" pointer of a valid object, even something as seemingly-benign as

    free(ptr);
    if (condition1) {foo(); ptr=NULL;}
    if (condition2) {bar(); ptr=NULL;}
    if (ptr != NULL) printf("Forgot to clear out pointer!");

may cause some compilers to infer that if condition1 is false, bar() should be
called unconditionally since the Standard would not require that the compiler
evaluate condition2 in such case.  Unfortunately, some compilers writers seem
to think that such "optimizations" are a good thing.

[toc] | [prev] | [next] | [standalone]


#67862

FromKeith Thompson <kst-u@mib.org>
Date2015-08-19 18:09 -0700
Message-ID<lnmvxm3gwa.fsf@kst-u.example.com>
In reply to#67860
supercat@casperkitty.com writes:
> On Friday, August 7, 2015 at 4:48:18 PM UTC-5, Keith Thompson wrote:
>> Accessing a pointer value after the object it points to no longer exists
>> has undefined behavior, even if you don't dereference it.  It's not
>> likely to do anything nasty on most systems, but in principle an
>> implementation could mark the pointer value as invalid and trap on any
>> reference to it.
>
> If a pointer isn't null, doesn't identify a valid object, and isn't a
> "one-past" pointer of a valid object, even something as seemingly-benign as
>
>     free(ptr);
>     if (condition1) {foo(); ptr=NULL;}
>     if (condition2) {bar(); ptr=NULL;}
>     if (ptr != NULL) printf("Forgot to clear out pointer!");
>
> may cause some compilers to infer that if condition1 is false, bar()
> should be called unconditionally since the Standard would not require
> that the compiler evaluate condition2 in such case.  Unfortunately,
> some compilers writers seem to think that such "optimizations" are a
> good thing.

The code has undefined behavior if condition1 and condition2 are both
false.

Since the code is incorrect, it shouldn't matter much how it behaves.
But it would be nice if the compiler printed a warning.

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

[toc] | [prev] | [next] | [standalone]


#67864

Fromglen herrmannsfeldt <gah@ugcs.caltech.edu>
Date2015-08-20 01:00 +0000
Message-ID<mr38rc$mbb$1@speranza.aioe.org>
In reply to#67862
Keith Thompson <kst-u@mib.org> wrote:
> supercat@casperkitty.com writes:

(snip)
>> If a pointer isn't null, doesn't identify a valid object, and isn't a
>> "one-past" pointer of a valid object, even something as seemingly-benign as

>>     free(ptr);
>>     if (condition1) {foo(); ptr=NULL;}
>>     if (condition2) {bar(); ptr=NULL;}
>>     if (ptr != NULL) printf("Forgot to clear out pointer!");

>> may cause some compilers to infer that if condition1 is false, bar()
>> should be called unconditionally since the Standard would not require
>> that the compiler evaluate condition2 in such case.  Unfortunately,
>> some compilers writers seem to think that such "optimizations" are a
>> good thing.
 
> The code has undefined behavior if condition1 and condition2 are both
> false.

I completely agree that it is official UB.

> Since the code is incorrect, it shouldn't matter much how it behaves.
> But it would be nice if the compiler printed a warning.

I suppose a warning is fine, but on most processors it isn't
all that hard to do what the program says. Doing the optimization
and then not warning about it is mean. Legal, but mean.

How about a special option that enables this kind of optimization,
so it can be turned off when one doesn't want it.

-- glen

[toc] | [prev] | [next] | [standalone]


#67865

FromKeith Thompson <kst-u@mib.org>
Date2015-08-19 20:25 -0700
Message-ID<lnio8a3akw.fsf@kst-u.example.com>
In reply to#67864
glen herrmannsfeldt <gah@ugcs.caltech.edu> writes:
> Keith Thompson <kst-u@mib.org> wrote:
>> supercat@casperkitty.com writes:
>
> (snip)
>>> If a pointer isn't null, doesn't identify a valid object, and isn't a
>>> "one-past" pointer of a valid object, even something as seemingly-benign as
>
>>>     free(ptr);
>>>     if (condition1) {foo(); ptr=NULL;}
>>>     if (condition2) {bar(); ptr=NULL;}
>>>     if (ptr != NULL) printf("Forgot to clear out pointer!");
>
>>> may cause some compilers to infer that if condition1 is false, bar()
>>> should be called unconditionally since the Standard would not require
>>> that the compiler evaluate condition2 in such case.  Unfortunately,
>>> some compilers writers seem to think that such "optimizations" are a
>>> good thing.
>  
>> The code has undefined behavior if condition1 and condition2 are both
>> false.
>
> I completely agree that it is official UB.
>
>> Since the code is incorrect, it shouldn't matter much how it behaves.
>> But it would be nice if the compiler printed a warning.
>
> I suppose a warning is fine, but on most processors it isn't
> all that hard to do what the program says. Doing the optimization
> and then not warning about it is mean. Legal, but mean.

What do you mean by "what the program says"?  As far as the C standard
is concerned, the program has no meaning if condition1 and condition2
are both false.

Even ignoring the optimizations permitted by the standard, if both
conditions are false the program accesses the value of a dangling
pointer.  On some plausible target systems, that could at least cause
the program to crash.

> How about a special option that enables this kind of optimization,
> so it can be turned off when one doesn't want it.

If a compiler vendor wants to provide such an option, that's fine with
me.

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

[toc] | [prev] | [next] | [standalone]


#67887

Fromsupercat@casperkitty.com
Date2015-08-20 08:15 -0700
Message-ID<5620ecc0-0e7e-42a7-b389-4028db865a11@googlegroups.com>
In reply to#67865
On Wednesday, August 19, 2015 at 9:33:27 PM UTC-5, Keith Thompson wrote:
> What do you mean by "what the program says"?  As far as the C standard
> is concerned, the program has no meaning if condition1 and condition2
> are both false.

There certainly exist platforms where comparing the pointer to null could trap,
and there may be some where it would cause non-deterministic behavior, and
there may thus be usefulness to allowing a dialect of C to be used on such
platforms.  That does not imply, however, that there would not be just as much
value in recognizing a dialect of C where pointer comparisons are guaranteed
not to cause Undefined Behavior.  I would consider the failure of the C
Standard to recognize behavior which the vast majority of compilers' dialects
have supported until recently to be a defect in the Standard.

>Even ignoring the optimizations permitted by the standard, if both conditions >are false the program accesses the value of a dangling
>pointer.  On some plausible target systems, that could at least cause
>the program to crash.

True.  On the other hand, while I didn't include an "exit" after the printf,
it's quite plausible that abnormal program termination at the indicated point
in the code would be better than proceeding, in which case the code's objective
would be met on such platforms, though perhaps less helpfully than if it
managed to output its message first.

Further, while some hardware platforms would crash when given such code, many
would merely indicate that anything which isn't a valid null pointer, isn't a
valid null pointer, thus yielding useful behavior.  C has historically been a
language where the fact that a feature wasn't available on all platforms would
not preclude programmers from using the feature if it existed on all the
platforms they were interested in.  If C programmers had always been limited to
those behaviors which were available on all platforms, the language would have
been dead by 1975.

[toc] | [prev] | [next] | [standalone]


#67895

FromKeith Thompson <kst-u@mib.org>
Date2015-08-20 11:38 -0700
Message-ID<lna8tl3iwn.fsf@kst-u.example.com>
In reply to#67887
supercat@casperkitty.com writes:
> On Wednesday, August 19, 2015 at 9:33:27 PM UTC-5, Keith Thompson wrote:
>> What do you mean by "what the program says"?  As far as the C standard
>> is concerned, the program has no meaning if condition1 and condition2
>> are both false.
>
> There certainly exist platforms where comparing the pointer to null could trap,
> and there may be some where it would cause non-deterministic behavior, and
> there may thus be usefulness to allowing a dialect of C to be used on such
> platforms.  That does not imply, however, that there would not be just as much
> value in recognizing a dialect of C where pointer comparisons are guaranteed
> not to cause Undefined Behavior.  I would consider the failure of the C
> Standard to recognize behavior which the vast majority of compilers' dialects
> have supported until recently to be a defect in the Standard.

The standard *does* recognize such behavior -- specifically by not
constraining the range of permitted behaviors in any way.

I'm not sure that the phrase "guaranteed not to cause Undefined
Behavior" makes sense.  Undefined behavior is not some particular
kind of behavior, like a program crash.  It means that the standard
refrains from specifying what the behavior is.

I think what you're actually suggesting is that a dialect of C
could define the behavior of accessing a pointer value after the
pointed-to object has reached the end of its lifetime.  That still
leaves open the question of *how* it's to be defined.  Would it
be implementation-defined or unspecified?  If so, the standard
would have to specify a set of permitted behaviors from which
an implementation may choose, with or without a requirement to
document its choice.  Or would some specific behavior be specified?
What would that behavior be?

I would argue that accessing the value of an indeterminate pointer:

    free(ptr);
    if (ptr == NULL) /* ... */

very probably indicates a bug in the program.  I want that bug
diagnosed by the compiler if possible.  Making the behavior undefined
permits compilers to, for example, generate code that causes the
program to die with a clear error message.  Making the behavior
well-defined means that compilers cannot reasonably diagnose
the logical error (since, as far as the language is concerned,
it wouldn't be an error).

Suppose a CPU is designed so that loading an invalid pointer into a
register causes a trap.  Defining the behavior of a accessing such
a pointer would mean that a compiler for such a CPU would have to
generate extra code to ensure that that trap doesn't happen.

There is one point on which I suspect we both agree: If a compiler
is clever enough to take advantage of undefined behavior to generate
*surprising* code (for example, code that executes unconditionally
even though it's protected by an "if"), it should issue a warning.
It may be difficult for the compiler to determine when such a
warning is appropriate -- and issuing too many warnings is almost
as bad as issuing too few.  But I'm sure there's plenty of work to
be done in this area.

[...]

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

[toc] | [prev] | [next] | [standalone]


#67903

Fromglen herrmannsfeldt <gah@ugcs.caltech.edu>
Date2015-08-20 18:47 +0000
Message-ID<mr57b8$527$1@speranza.aioe.org>
In reply to#67895
Keith Thompson <kst-u@mib.org> wrote:
> supercat@casperkitty.com writes:
>> On Wednesday, August 19, 2015 at 9:33:27 PM UTC-5, Keith Thompson wrote:
>>> What do you mean by "what the program says"?  As far as the C standard
>>> is concerned, the program has no meaning if condition1 and condition2
>>> are both false.

>> There certainly exist platforms where comparing the pointer to 
>> null could trap, and there may be some where it would cause 
>> non-deterministic behavior, and there may thus be usefulness 
>> to allowing a dialect of C to be used on such platforms.  

(snip)
> The standard *does* recognize such behavior -- specifically by not
> constraining the range of permitted behaviors in any way.
 
> I'm not sure that the phrase "guaranteed not to cause Undefined
> Behavior" makes sense.  Undefined behavior is not some particular
> kind of behavior, like a program crash.  It means that the standard
> refrains from specifying what the behavior is.
 
> I think what you're actually suggesting is that a dialect of C
> could define the behavior of accessing a pointer value after the
> pointed-to object has reached the end of its lifetime.  That still
> leaves open the question of *how* it's to be defined.  Would it
> be implementation-defined or unspecified?  If so, the standard
> would have to specify a set of permitted behaviors from which
> an implementation may choose, with or without a requirement to
> document its choice.  Or would some specific behavior be specified?
> What would that behavior be?

At some point, as with many other things, it depends on the 
processor. Consider overflow, which might be less interesting
than pointers. Some trap on overflow, some don't, and many people
know what their processor does. 
 
> I would argue that accessing the value of an indeterminate pointer:
 
>    free(ptr);
>    if (ptr == NULL) /* ... */
 
> very probably indicates a bug in the program.  I want that bug
> diagnosed by the compiler if possible.  

No complaint here.

> Making the behavior undefined permits compilers to, for example, 
> generate code that causes the program to die with a clear error 
> message.  

But that isn't the discussion here.

> Making the behavior well-defined means that compilers cannot 
> reasonably diagnose the logical error (since, as far as the 
> language is concerned, it wouldn't be an error).

The case at hand is where compilers make optimizations based
on the UB, often with no message, and surprising results.
 
> Suppose a CPU is designed so that loading an invalid pointer into a
> register causes a trap.  Defining the behavior of a accessing such
> a pointer would mean that a compiler for such a CPU would have to
> generate extra code to ensure that that trap doesn't happen.

Well, x86 for x>=2. But compilers normally don't load segment
registers when not needed. (For one reason, it is slower.) 
Copying pointers in x86 (for any x) is normally done without
loading them into segment registers. There are no compare 
instructions for segment registers, so that isn't even a choice.
 
> There is one point on which I suspect we both agree: If a compiler
> is clever enough to take advantage of undefined behavior to generate
> *surprising* code (for example, code that executes unconditionally
> even though it's protected by an "if"), it should issue a warning.
> It may be difficult for the compiler to determine when such a
> warning is appropriate -- and issuing too many warnings is almost
> as bad as issuing too few.  But I'm sure there's plenty of work to
> be done in this area.

As far as I know, compiler technology isn't to the point where
such optimizations aren't done without the help of poeple. 
But maybe not so much longer. With an appropriate machine readable
form of the operation of each instruction, and without any thought
to how it might be used, it could happen.

-- glen

[toc] | [prev] | [next] | [standalone]


#67910

Fromsupercat@casperkitty.com
Date2015-08-20 14:01 -0700
Message-ID<7f4a4aa6-c2bc-4867-8019-9a824ec30972@googlegroups.com>
In reply to#67895
On Thursday, August 20, 2015 at 12:40:26 PM UTC-5, Keith Thompson wrote:
> I think what you're actually suggesting is that a dialect of C
> could define the behavior of accessing a pointer value after the
> pointed-to object has reached the end of its lifetime.  That still
> leaves open the question of *how* it's to be defined.  Would it
> be implementation-defined or unspecified?  If so, the standard
> would have to specify a set of permitted behaviors from which
> an implementation may choose, with or without a requirement to
> document its choice.  Or would some specific behavior be specified?
> What would that behavior be?

If I were king of the world, there would be a standardized set of macros which
applications could use to indicate what behaviors they could accept for various
actions that presently invoke totally-Undefined Behavior.  Among the choices
would be "anything" [hyper-modern UB would be acceptable], "anything, but
sequenced" [implying that the compiler could do anything once program execution
reached the point of the behavior, but requiring the compiler to allow for the
possibility that some factor otherwise unknown to it might terminate the
program before it could get that far], "generate idiomatic code for underlying
processor" [available as an option only on processors with a clear idiomatic
behavior], and various other options specific to the action in question (e.g.
for oversized shifts, I'd include options to yield the shift-mod-wordsize value, or the repeated-shift-by-one value, and allow code to bitwise-OR those choices if either would be acceptable).

> Making the behavior
> well-defined means that compilers cannot reasonably diagnose
> the logical error (since, as far as the language is concerned,
> it wouldn't be an error).

I would offer choices for various presently-UB relational and equality
comparisons--programmers could indicate whether they are happy with
having them arbitrarily yield 1 or 0, whether they need to be ranked but
may overlap, whether they need them to be ranked and (for valid pointers)
non-overlapping, whether it would be acceptable for them to trap in
implementation-defined fashion, etc.  Not all platforms could implement all
behaviors that programmers might want, but it should be possible for programmers
to make use of any guarantees their platform can provide.

> Suppose a CPU is designed so that loading an invalid pointer into a
> register causes a trap.  Defining the behavior of a accessing such
> a pointer would mean that a compiler for such a CPU would have to
> generate extra code to ensure that that trap doesn't happen.

What is necessary is not to require that all compilers guarantee particular
behaviors for particular actions, but merely that compilers which cannot provide
various guarantees **indicate their inability to do so**.  If code is unlikely
to ever run on a processor which can't uphold guarantees it would require, it
would make far more sense to have the program indicate its requirements, and
run on platforms that can meet them, and only rework the logic to work without
such guarantees if the program actually needs to run on a platform that can't
meet them, then to require that a program use a sub-optimal algorithm because
the optimal algorithm would require behavioral guarantees which the underlying
hardware could meet easily, but which isn't required by the C Standard.

The reason C has historically thrived is that the inability of some platforms
to make useful behavioral guarantees in some situations has not prevented
code which didn't have to run on those platforms from making use of those
guarantees, and the fact that some platforms can't uphold guarantees that some
programs require won't prevent those platforms from being used to run programs
that don't require those guarantees.

Had it not been possible for programmers to make use of platform-specific
guarantees beyond those imposed by the Standard, C would never have gained
enough prominence to be worth even a historical footnote.

> There is one point on which I suspect we both agree: If a compiler
> is clever enough to take advantage of undefined behavior to generate
> *surprising* code (for example, code that executes unconditionally
> even though it's protected by an "if"), it should issue a warning.
> It may be difficult for the compiler to determine when such a
> warning is appropriate -- and issuing too many warnings is almost
> as bad as issuing too few.  But I'm sure there's plenty of work to
> be done in this area.

The problem is intractable with the language as it is, since a compiler has no
way of knowing a program's requirements, nor what sort of input is expected.
Without knowing such things, there's no way an optimizer can distinguish things
the programmer would like to be warned about from things the programmer already
knows.

[toc] | [prev] | [next] | [standalone]


#67889

Fromglen herrmannsfeldt <gah@ugcs.caltech.edu>
Date2015-08-20 16:43 +0000
Message-ID<mr5043$gfe$1@speranza.aioe.org>
In reply to#67865
Keith Thompson <kst-u@mib.org> wrote:
> glen herrmannsfeldt <gah@ugcs.caltech.edu> writes:

(snip, someone wrote)
>>>>     free(ptr);
>>>>     if (condition1) {foo(); ptr=NULL;}
>>>>     if (condition2) {bar(); ptr=NULL;}
>>>>     if (ptr != NULL) printf("Forgot to clear out pointer!");

(snip)
>> I completely agree that it is official UB.

>>> Since the code is incorrect, it shouldn't matter much how it behaves.
>>> But it would be nice if the compiler printed a warning.

>> I suppose a warning is fine, but on most processors it isn't
>> all that hard to do what the program says. Doing the optimization
>> and then not warning about it is mean. Legal, but mean.
 
> What do you mean by "what the program says"?  As far as the C standard
> is concerned, the program has no meaning if condition1 and condition2
> are both false.

Yes.
 
> Even ignoring the optimizations permitted by the standard, if both
> conditions are false the program accesses the value of a dangling
> pointer.  On some plausible target systems, that could at least cause
> the program to crash.

Yes, but if it isn't on one of those systems, and even if it is,
they can often get around it. 80286 protected mode compilers will
avoid loading selectors into segment registers to copy or compare
the value. (There aren't compare instructions for them, anyway.)

For those using C as a replecement for assembly, they often know
exactly what the generated code will look like. In the case of
device drivers, they aren't portable anyway, so there is no need
to worry about that. 

I am completely against programs that dereference pointers that
aren't pointing to the right place, but not so much against ones
that copy or compare pointers.
 
>> How about a special option that enables this kind of optimization,
>> so it can be turned off when one doesn't want it.
 
> If a compiler vendor wants to provide such an option, that's fine with
> me.

-- glen

[toc] | [prev] | [next] | [standalone]


Page 1 of 2  [1] 2  Next page →

Back to top | Article view | comp.lang.c


csiph-web