C++ Logo

std-proposals

Advanced search

Re: [std-proposals] about incrementing a pointer of a type with pure virtual function

From: Chris Ryan <chrisr98008_at_[hidden]>
Date: Fri, 20 Jan 2023 12:37:41 -0800
As so many people said there are so many things wrong with this code but
the fundamental error is a pointer math issue.
When the array b is passed to f(...) the array degrades to a pointer to the
head of the array b.
It does not pass an array of B objects. It only points to the head of the
array.

(on my system with a 64-bit compiler)
class B has a size of 32 bytes.
so the full array b is size 64 (two elements of B)
class I has a size of 8 bytes (the size of the v-table).

Inside the function void f(I*) it has no idea that it is pointing at a B
structure.
It thinks it is pointing at an I object (or as we are interpreting it a
block of I objects)
When you do i++ you add an increment size of I (8 bytes) to i, (NOT the 32
bytes of the size of B)
so it is now not pointing at anything meaningful. (middle of a B object)

In the next iteration of the loop it tries to call a virtual function
A::f() (that would virtual redirect to B::f() ) on the pointer i.
This attempts to look up the v-table to look up the function pointer for
the A::f() function.
But the pointer i is not pointing at an instance of a class with a v-table,
so ka-boom.

It did not crash on the call to i->print(); because the i value aka the
'this' in the member function was never validated,
it was just printed a value of the "this" (aka the i it was called with)
that is not meaningful anything meaningful.
(Undefined Behaviour, calling a member function with an invalid this
pointer)

Had you tried to dereference the i using array subscripts, i[j]->f();
you would get the same error because array subscripts also use the same
increment size of I (8 bytes.)

To visualize this draw an array of 64 bytes. (two blocks of 32, one for
each B)
Have "b" point at the beginning of the array.
When you call f(b) in main, the function void f(I* i) just uses the same
value of b (cast to a type of an I) as i.
The first time "i" is pointing at the head of a B object and able to access
the v-table.
Now in your drawing move the i pointer forward 8 bytes (aka i++) and tell
me where your pointer is pointing,
It is in the middle of an instance of a B object, not the next instance of
an I object. (Boom)






On Thu, Jan 19, 2023 at 10:56 PM Julien Allali via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> Hi everyone,
>
> I checked the following code from one of my student:
>
> class I{
> public:
> virtual ~I(){}
> virtual void print(){
> printf("hello %p\n",this);
> }
> virtual void f()=0;
> };
>
> class B : public virtual I{
> int i,j;
> public:
> ~B(){}
> virtual void f(){
> printf("B %p\n",this);
> }
> };
>
>
> void f(I *i){
> for(int j=0;j<5;++j)
> {
> i->print();
> i->f();
> printf("i=%p ",i);
> i++; // PROBLEM IS HERE
> printf(" => %p \n",i);
> }
> }
>
> int main(){
> B b[]={B(),B()};
> printf("B: %p %p\n",b,b+1);
> f(b);
> }
>
> As you can see, I is a non instantiable type as it has one pure virtual
> method. My concern is about the line "i++": I believed it will lead to
> an error or at least a warning (g++ 11.3.0)... Indeed, I can not imagine
> a valid case where doing arithmetic on a pointer of a type with pure
> virtual function can be valid.... Shouldn't the standard forbid such
> arithmetic?
>
> Julien.
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>

Received on 2023-01-20 20:37:55